reentrantlock 通过线程私有的 holdcount 实现可重入,首次加锁设 exclusiveownerthread 并置 holdcount=1,重入时仅 increment,unlock 逐次 decrement,归零才释放锁。

ReentrantLock 能让同一个线程反复加锁,核心靠的是每个线程私有的 holdCount(持有计数器)。它不是全局变量,也不跨线程共享,而是绑定在线程与锁的关联关系上——即“当前线程是否持有该锁”+“持有了几次”,两者共同决定行为。
holdCount 是怎么工作的
ReentrantLock 内部维护两个关键字段:exclusiveOwnerThread(独占线程)和 holdCount(持有次数)。它们协同完成可重入逻辑:
- 线程首次调用
lock():若锁空闲,exclusiveOwnerThread被设为当前线程,holdCount = 1 - 同一线程再次调用
lock():发现exclusiveOwnerThread == 当前线程,跳过阻塞,仅执行holdCount++ - 每次调用
unlock():先检查当前线程是否真是持有者(否则抛IllegalMonitorStateException),再执行holdCount-- - 只有当
holdCount减至 0 时,exclusiveOwnerThread才被清空,锁才真正释放,其他线程才可能获取
为什么递归调用不会死锁
递归方法中反复进入加锁区域,比如:
void recursiveTask(int n) {
lock.lock(); // 第1次:holdCount=1;第2次:holdCount=2;……
try {
if (n > 0) recursiveTask(n - 1);
} finally {
lock.unlock(); // 每层对应一次 unlock,holdCount 逐层减1
}
}
关键在于:每次进入都只做「身份校验 + 计数器自增」,不触发排队或阻塞。JVM 不需要介入,全由 ReentrantLock 的 AQS 同步器在用户态完成。只要递归出口前 unlock 次数匹配 lock 次数,计数器终将归零。
如何验证和调试 holdCount
开发中可通过公开 API 实时观察状态,避免因漏解锁导致锁长期占用:
-
getHoldCount():返回当前线程对该锁的持有次数(仅对当前线程有效) -
isHeldByCurrentThread():快速判断是否已持锁,适合在递归入口做 guard - 示例:递归前加检查
if (!lock.isHeldByCurrentThread()) lock.lock();,可避免无谓的计数虚高
容易出错的边界情况
holdCount 机制虽可靠,但使用不当仍会导致问题:
- 未在
finally中调用unlock():异常抛出后计数器卡住,锁无法释放 - 混用
tryLock()和lock():tryLock() 在非公平模式下虽支持重入,但返回 false 时不会增加 holdCount,逻辑易混乱 - 跨线程误查
getHoldCount():该方法只对当前线程有意义,其他线程调用始终返回 0










