ReentrantLock 的重入特性允许同一线程多次获取同一把锁而不阻塞,依赖 exclusiveOwnerThread 和 holdCount 实现;每次 lock() 增计数,unlock() 减计数,仅当计数归零才真正释放锁;需严格配对调用并避免跨线程使用。

ReentrantLock 的重入特性允许同一线程多次获取同一把锁,递归调用时不会因重复加锁而阻塞,核心在于它通过持有计数(hold count)记录当前线程获取锁的次数。
重入机制依赖线程身份与计数器
ReentrantLock 内部维护一个 exclusiveOwnerThread 字段和一个 holdCount 计数器。当同一线程首次调用 lock() 时,exclusiveOwnerThread 被设为当前线程,holdCount 置为 1;后续每次成功加锁,仅 holdCount 自增,不阻塞也不新建锁状态。
- 每次
unlock()都使 holdCount 减 1,只有当 holdCount 降为 0 时,exclusiveOwnerThread 才被清空,锁才真正释放 - 其他线程尝试获取该锁时,会检查 exclusiveOwnerThread 是否为自身 —— 若是则直接通过,否则需等待或失败(取决于是否使用 tryLock)
递归调用中锁的获取与释放必须配对
在递归方法中,每次进入都应对应一次 lock(),每次退出前应有对应 unlock(),否则会导致 holdCount 不归零,锁无法释放,造成死锁或资源泄漏。
- 推荐使用 try-finally 结构确保 unlock 执行,尤其在多层递归中容易遗漏异常路径
- 错误示例:递归入口加锁但只在最外层释放,内层未 unlock → holdCount 始终 > 0,锁未真正释放
- 正确做法:每层递归独立完成 lock/unlock,或统一在出口处按加锁次数逐次 unlock
不可跨线程重入,且不支持条件变量混用
重入只对“同一个线程”生效。若递归过程中切换线程(如 ForkJoinPool 提交子任务),新线程调用 lock() 会被阻塞,即使原线程仍持有锁。
立即学习“Java免费学习笔记(深入)”;
- Condition 对象也绑定到特定 ReentrantLock 实例,其 await/signal 必须在持有该锁的前提下调用,递归中若锁状态不稳定,易引发 IllegalMonitorStateException
- 注意:synchronized 也有类似重入行为,但 ReentrantLock 的显式计数更透明,便于调试和监控(如 getHoldCount())
实际递归场景建议谨慎使用
虽然语法上可行,但递归 + 显式锁容易破坏可读性与可维护性,深度过大还可能触发栈溢出或锁持有时间过长。
- 优先考虑将递归逻辑改为迭代,并配合锁保护共享状态
- 若必须递归,可将锁范围缩小到临界区最小粒度,避免整个递归链持锁
- 测试时注意模拟深层递归,验证 holdCount 变化与最终释放是否符合预期


















