目录
什么是可重入锁(递归锁)
sychronized版
lock版
为什么可重入锁能防止死锁
什么是可重入锁(递归锁)
可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁
可重入锁最大的作用就是避免死锁
看这个小例子,method01和02都是加锁的方法,根据可重入锁的原理,线程只要获得了01的锁就能访问02,即使02也是个带锁的方法。
sychronized版
一个同步方法可进入另外一个同步方法
// Synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sms();
}, "A").start();
new Thread(() -> {
phone.sms();
}, "B").start();
}
}
class Phone {
public synchronized void sms() {
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 这里也有锁
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "call");
}
}
lock版
sms方法是一个lock锁方法,这个方法调用了call方法,而call方法也是个lock锁方法。可重入锁指一个线程只要能进入sms方法,就也能进入到call方法中
public class Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(() -> {
phone.sms();
}, "A").start();
new Thread(() -> {
phone.sms();
}, "B").start();
}
}
class Phone2 {
Lock lock = new ReentrantLock();
public void sms() {
lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否则就会死在里面
try {
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 这里也有锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
为什么可重入锁能防止死锁
这里举一个经典的例子。
public class Widget {
public synchronized void doSomething(){
// do something
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
super.doSomething();
}
}
现在假设synchronized不是可重入锁。
Widget方法执行需要获得sync锁,默认情况下sync锁的是this,LoggingWidget是Widget的子类。当你执行LoggingWidget中的super.doSomething方法时它就会获得父类的this锁,但父类的this锁被子类抢走之后,没了锁就没法执行完毕。
这样就出现死锁的现象,子类让父类执行完释放锁,而父类的锁在子类这里没法执行完毕。
现在synchronized是可重入锁
当进入到LoggingWidget方法时,即使LoggingWidget方法里面有一另一个锁也没关系,它也能获得Widget类中的内容,由于Widget类执行完毕了,LoggingWidget便可以继续执行。