了解Lock锁的使用
简单介绍
1.在Java多线程中,可以使用synchronized关键字来实现线程之间同步互斥.
2.在JDK1.5中新增了ReentrantLock类也能达到此效果,并且在扩展功能上也更加强大,比如多路分支condition通知等功能,而且在使用上比synchronized更加灵活.
lock锁的示例
代码示例:
/**
* 示例reentrantLock类的使用.
*/
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
try {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() + " " + (i + 1));
}
} finally {
lock.unlock();
}
}
}
//定义一个线程
public class MyThread extends Thread {
private MyService myService;
public MyThread(MyService myService, String ThreadName) {
this.myService = myService;
super.setName(ThreadName);
}
@Override
public void run() {
super.run();
myService.testMethod();
}
}
测试类:
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
MyThread myThread1 = new MyThread(myService, "myThread1");
MyThread myThread2 = new MyThread(myService, "myThread2");
MyThread myThread3 = new MyThread(myService, "myThread3");
MyThread myThread4 = new MyThread(myService, "myThread4");
MyThread myThread5 = new MyThread(myService, "myThread5");
myThread1.start();
myThread2.start();
myThread3.start();
myThread4.start();
myThread5.start();
}
}
测试结果:
结论:
1.从运行上看,当前线程打印完毕之后将锁进行释放,其他线程才能继续打印.
2.说明ReentrantLock类和synchronized都可以实现线程之间同步互斥.
两个常见异常
1.如果没有获取锁就释放锁,会报异常.
代码示例:
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
try {
// lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() + " " + (i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
执行结果:
2.ReentrantLock类是显示的指定锁和释放锁,如果我们不小心没有释放锁,那将会一直阻塞,出现异常也不会释放锁.
代码示例:
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
try {
lock.lock();
int s = 1 / 0;
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() + " " + (i + 1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// lock.unlock();
}
}
}
执行结果:
从运行看,程序并没有停止,我们通过jps查看当前进程.
然后通过jstack查看当前进程线程中的状态
结论:可以看到其他线程都在等待中, 说明如果没有执行lock.unlock();方法释放锁,即使出现异常也不会释放锁,在实际应用中要尤为注意这点,避免出现不必要的程序错误
Condition等待通知机制的示例
1.关键字synchronized与wait() 和notify() / notifyAll() 方法相结合可以实现等待通知模式.
2.类ReentrantLock也可以实现相同的功能,但是需要借助Condition对象.
代码示例:
/**
* TODO
* 实现生产者/消费者模式:一对一交替打印.
*
* @author 86182
* @version V1.0
* @since 2020-08-25 15:01
*/
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
condition.await();
}
System.out.println("打印❤");
hasValue = true;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("打印※");
hasValue = false;
condition.signal();
// condition.signalAll(); 为唤醒当前condition对象的所有线程.
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
定义线程类:
/**
* TODO
* ThreadA
* @author 86182
* @version V1.0
* @since 2020-08-25 15:06
*/
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
myService.set();
}
}
}
/**
* TODO
* ThreadB
* @author 86182
* @version V1.0
* @since 2020-08-25 15:07
*/
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
myService.get();
}
}
}
测试类:
/**
* TODO
*
* @author 86182
* @version V1.0
* @since 2020-08-25 15:08
*/
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
threadA.start();
ThreadB threadB = new ThreadB(myService);
threadB.start();
}
}
执行结果:
总结:
1.从运行结果上看,是一对一交替打印.
2.Condition对象是具有更好的灵活性,在一个Lock对象中可以创建多个Condition实例,线程对象可以注册在指定的Condition对象中,从而可以有选择性的通知线程,在调度上更加灵活.