什么是锁的可重入性?
什么是锁的可重入性?
锁的“可重入性”(reentrancy)指:同一线程在已经获得某把锁的情况下,可以“再次”获得这把锁而不会被自己阻塞。实现上通常会为锁维护“持有线程 + 重入计数(hold count)”:同一线程每 lock()/进入一次同步块,计数+1;每次 unlock()/退出一次,计数−1;直到归零才真正释放。
为什么需要可重入?
很多代码会层层调用且每层都做同步保护。如果锁不可重入,同一线程第二次进入会卡死自己。可重入能让这类嵌套调用安全工作。
典型例子(可重入的内置锁 synchronized)
public class ReentrantDemo {
public synchronized void a() { // 拿到 this 的监视器
b(); // 再次尝试进入同一把锁(this)
}
public synchronized void b() { // 可重入:不会阻塞自己
// ...
}
}
a() 已经拿了 this 的监视器,调用 b() 时再次进入同一把锁,计数从 1 变 2,返回时各自退出一次,最终释放。
ReentrantLock 同理
ReentrantLock lock = new ReentrantLock();
void a() {
lock.lock();
try { b(); }
finally { lock.unlock(); } // 计数 -1
}
void b() {
lock.lock(); // 计数 +1(同一线程)
try { /* ... */ }
finally { lock.unlock(); } // 计数 -1
}
还可以查询:
lock.isHeldByCurrentThread(); // 是否当前线程持有
lock.getHoldCount(); // 重入层数
不可重入会怎样?
自实现一把不可重入的锁(只记录“是否占用”,不记录“谁占用”和“重入计数”)会导致同线程二次进入自锁死:
class NonReentrantLock {
private volatile boolean locked = false;
public synchronized void lock() throws InterruptedException {
while (locked) wait();
locked = true;
}
public synchronized void unlock() {
locked = false;
notify();
}
}
NonReentrantLock l = new NonReentrantLock();
void a() throws Exception {
l.lock();
try { b(); } finally { l.unlock(); }
}
void b() throws Exception {
l.lock(); // 同一线程再次 lock,发现 locked=true,自己在 wait(),→ 永久阻塞
try { } finally { l.unlock(); }
}
这就是“不可重入”的危险。
可重入锁与“可重入代码”不是一回事
-
可重入锁:同一线程可重复获得同一把锁。
-
可重入(reentrant)代码/函数(计算机体系结构语境):函数在被多个线程/中断并发调用时不依赖共享可变状态,因而是线程安全/可重入的。这是不同概念。
常见锁的可重入性
-
synchronized(对象监视器/内置锁):✅ 可重入(JVM 维护持有者与计数)。 -
ReentrantLock:✅ 可重入(AQS 维护 owner 与 hold count,支持公平/非公平、可中断、可定时)。 -
Semaphore:❌ 不可重入(它是配额计数器,不跟线程绑定;同线程 acquire 多次会消耗多个许可,逻辑上不是“重入”)。 -
ReadWriteLock:实现视具体类(如ReentrantReadWriteLock的同线程读锁可重入,写锁对同线程也可重入且支持写降级为读)。
设计与使用要点
-
重入计数必须匹配释放次数
每次成功lock()/进入同步块都要对应一次unlock()/退出;否则会漏释放(计数不归零,其他线程永远等)。 -
可重入不是“万能钥匙”
它解决的是“同一线程的嵌套进入问题”,不能避免不同线程之间的死锁(交叉加锁顺序不一致照样死锁)。 -
层层调用的同步策略
-
若上层已持有锁,下层方法就不要再对同一资源加另一把不同的锁(避免锁顺序复杂化)。
-
如需多把锁,统一顺序获取,降低死锁概率。
- 诊断
-
ReentrantLock#getHoldCount()有助于排查“忘记解锁”。 -
线程 dump 可观察某线程在持有锁还在等待同一把锁时(不可重入实现下的自锁死)。
一句话记忆
可重入 = “同一线程可在未释放的情况下再次进入同一把锁”,靠“持有者 + 计数”实现;它防止“自己把自己锁死”,但并不自动避免多线程之间的死锁。
浙公网安备 33010602011771号