可重入锁的引入:不可重入锁中的死锁问题
重入锁(ReentrantLock),顾名思义,就是支持重复进入的锁,它表示该锁能够支持同一个线程对一个资源的重复加锁。也就是说,如果某个线程试图获得一个已经由它自己持有的可重入锁,那个这个请求就会成功。
相反,不可重入锁就是指该锁不支持同一个线程对一个资源的重复加锁,由于 Java 中并没有现成的可重入锁可供使用,下面简单实现一个:
1 |
|
其中,isLocked 变量用于表示锁是否被占用的标志,lock 和 unlock 方法都是同步方法,确保在多线程环境下只有一个线程可以获得/释放锁,当锁已经被占用时,线程将通过调用在 while 循环中调用 wait 方法进行等待,直到锁被释放。
之后,我们再来看一个「不可重入锁中的死锁问题」:
下面是一个 Parent 类,它包含了一个不可重入锁 nonReentrantLock 和一个 method 方法 。在 method 方法中使用不可重入锁加锁,并且打印一条语句,之后在 finally 块中释放锁。
1 |
|
现在有一个 Child 类,它继承自 Parent ,并覆盖了 method 方法,并且也在其中使用了不可重入锁进行加锁。
1 |
|
下面编写测试类进行测试:
1 |
|
输出:

可以看到,程序只显示了 Child 类的打印语句,并且一直在运行,说明发生了死锁。
分析:
由于 Child 和 Parent 类中的 method 方法执行时都需要先加锁,即每个 method 方法在执行前都会获取 Parent 中的 nonReentrantLock 锁。
当程序运行到 Child 类的 super.method()
时,因为 nonReentrantLock 锁是不可重入锁,且线程在调用 child.method()
时已经持有了这个锁,所以线程会在此时永远停顿下去,等待一个永远也无法获得的锁。进而发生死锁。
解决:
想要解决这个问题非常简单:使用可重入锁。例如使用 ReentrantLock ,或者仅使用 synchronized 关键字修饰两个类的 method 方法即可。