重入锁死(Reentrance Lockout)

更新于 2025-12-29

Jakob Jenkov 2014-06-23

重入锁死(Reentrance Lockout)是一种与死锁(Deadlock)嵌套监视器锁死(Nested Monitor Lockout)类似的情况。在关于锁(Locks)读写锁(Read / Write Locks)的文章中,也部分涉及了重入锁死的内容。

当一个线程试图重新进入一个不可重入LockReadWriteLock 或其他同步器时,就可能发生重入锁死。所谓“可重入”(reentrant),是指一个已经持有某个锁的线程可以再次获取该锁。Java 中的 synchronized 块是可重入的。因此,下面这段代码可以正常运行,不会出现问题:

public class Reentrant {
    public synchronized void outer() {
        inner();
    }

    public synchronized void inner() {
        // 执行某些操作
    }
}

注意,outer()inner() 都被声明为 synchronized,这在 Java 中等价于 synchronized(this) 块。如果一个线程调用了 outer(),那么它在 outer() 内部调用 inner() 是没有问题的,因为这两个方法(或代码块)都是在同一个监视器对象(即 this)上同步的。

如果一个线程已经持有了某个监视器对象的锁,那么它就有权访问所有在该监视器对象上同步的代码块。这种特性称为重入性(reentrance)。线程可以重新进入任何它已经持有锁的代码块。

然而,下面这个 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() 将会阻塞。这时就发生了重入锁死


如何避免重入锁死?

要避免重入锁死,你有两个选择:

  1. 避免编写会重复进入锁的代码
  2. 使用可重入锁

哪种方案更适合你的项目,取决于具体情况。通常,可重入锁的性能不如非可重入锁,并且实现起来更复杂。但在你的应用场景中,这可能并不是一个问题。是否使用支持重入的锁,需要根据具体情况进行权衡。