可重入锁解决递归加锁问题,核心是允许同一线程多次获取同一锁而不阻塞;它通过记录持有线程ID和重入次数实现:首次加锁设线程并计数为1,再次加锁仅计数加1,unlock只减计数,归零才释放锁。

Java 中用可重入锁解决递归调用加锁问题,核心在于允许同一线程多次获取同一把锁而不阻塞自己。只要锁对象一致,外层方法持有了锁,内层(包括递归调用)再请求该锁时直接成功,只增加内部计数器,不会死锁。
为什么递归调用需要可重入锁
普通不可重入锁在同一线程重复加锁时会因“自己等自己”而永久阻塞——比如方法 A 加锁后调用自身或另一个也加同一把锁的方法 B,B 尝试加锁就会卡住,A 无法继续执行也无法释放锁,形成死锁。
可重入锁通过记录 持有线程 ID + 重入次数 来避免这个问题:首次加锁设线程为持有者、计数为 1;再次加锁发现是同一个线程,就只将计数加 1;每次 unlock 只减 1,直到归零才真正释放锁。
synchronized 天然支持可重入
Java 内置的 synchronized 关键字就是可重入锁,无需额外配置:
立即学习“Java免费学习笔记(深入)”;
- 锁对象必须是同一个(如 this、类.class 或指定对象)
- 方法嵌套调用或递归调用中,只要同步范围使用同一把锁,就能顺利进入
示例:
class Phone {public synchronized void sms() {
System.out.println("sms");
call(); // 同一线程再次进入 synchronized 方法
}
public synchronized void call() {
System.out.println("call");
}
}
ReentrantLock 显式控制可重入行为
ReentrantLock 是显式锁,需手动 lock()/unlock(),但它同样可重入:
- 同一线程连续调用 lock(),会成功并使内部计数递增
- 必须保证 lock() 和 unlock() 成对出现(推荐用 try-finally)
- 支持公平/非公平策略,但可重入性与公平性无关
示例(递归场景):
class Counter {private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void recursiveInc(int n) {
lock.lock();
try {
count++;
if (n > 1) recursiveInc(n - 1); // 递归中再次 lock()
} finally {
lock.unlock(); // 每次对应一次 lock(),计数减 1
}
}
}
关键注意事项
可重入不等于线程安全自动升级,仍需注意:
- 锁对象必须一致:synchronized(this) 和 synchronized(obj) 不是同一把锁
- 避免忘记 unlock():ReentrantLock 不释放会导致其他线程永远等待
- 重入次数过多可能引发栈溢出(如深度递归 + 每层 lock),但这是逻辑问题,不是锁机制缺陷
- synchronized 的可重入由 JVM 在 monitor 对象中维护计数器实现;ReentrantLock 则在 AQS 的 state 字段和 exclusiveOwnerThread 字段中维护


















