Reentrance Lockout的情况类似于死锁和nested monitor lockout.Reentrance Lockout在先前的Java中的锁和Java中的读写锁两篇文章中都有提到.
线程在多次获取锁和读写锁实例或是其他不支持可重入的同步机制时会陷入阻塞即锁死.可重入意味着线程可以多次持有同一把锁.Javasynchronized
同步块是可重入的.下面给出可重入的示例代码:
public class Reentrant{
public synchronized void outer() {
inner();
}
public synchronized void inner() {
// do something you like
}
}
我们可以注意到outer()和inner()方法签名中都有synchronized
声明,这在Java中等同于synchronized(this)
同步代码块。当多个方法都是以"this"即当前对象作为监控对象时,那么一个线程在调用完outer()方法后,自然可以在outer()方法内部调用inner()方法。一个线程将一个对象作为监控对象即取得该对象锁后,它可以进入其他使用相同对象作为对象锁的同步代码块。这种情况我们称之为可重入。线程可以反复进入它所取得监控对象的全部同步代码块。
在以往文章总提到过Lock的实现是不可重入的,如下:
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
如果一个线程在没有调用unlock()的情况下,两次掉调用lock()方法,那么它将阻塞在第二次lock()方法调用上.这时候reentrance lockout 就会发生了.
面对这种情况我们有两种选择:
- 不要写多次获取获取锁实例的代码
- 使用可重入锁
你应该根据实际情况来选择.可重入锁需要提供额外的功能来支持可重入性,因此通常会比不可重入锁性能要稍差点.还有可重入锁通常比较难实现,但幸好Java已经提供了相应的实现了.无论你的代码实现可重入性如何你都需要谨慎使用可重入锁.
该系列博文为笔者复习基础所著译文或理解后的产物,复习原文来自Jakob Jenkov所著Java Concurrency and Multithreading Tutorial