reentrantlock通过thread.currentthread()判断同一线程,支持线程池复用但禁止跨线程传递;重入时lock()直接通过,trylock()立即返回true,getholdcount()仅对当前线程有效,unlock()必须与lock()严格配对。

ReentrantLock 是怎么判断“同一个线程”的
它靠的是 Thread.currentThread() 返回的线程对象引用,不是线程名、ID 或其他标识。只要两次调用都发生在同一个 Thread 实例里,就认为是可重入。
这意味着:线程池中复用的线程(比如 ThreadPoolExecutor 里的工作线程)也能正常重入;但如果你手动把锁从一个线程传给另一个线程(哪怕只是传了引用),就会抛 IllegalMonitorStateException。
- 不要在
Runnable或Callable中把ReentrantLock实例暴露给别的线程 - 避免在异步回调(如
CompletableFuture.thenAccept)里误用同一把锁——回调很可能在别的线程执行 -
lock.isHeldByCurrentThread()可以安全用于调试,但它只反映当前线程是否持有该锁,不反映重入次数
lock() 和 tryLock() 在重入场景下的行为差异
lock() 会一直阻塞直到获取成功,重入时直接通过、不排队;tryLock() 立即返回布尔值,重入也返回 true,但要注意它不支持带超时的重入检测逻辑——tryLock(long, TimeUnit) 在重入时仍可能因等待队列头节点而超时(即使当前线程已持锁)。
- 重入时
tryLock()总是立即返回true,无需担心“被自己阻塞” - 但
tryLock(1, TimeUnit.SECONDS)在锁被其他线程持有时会等满 1 秒,哪怕你当前线程已经持有了——它不区分“自己持锁”和“别人持锁” - 如果想做“仅当未被任何线程持有才获取”,必须配合
isLocked()+tryLock()组合,但存在微小竞态窗口
重入计数器藏在哪?如何安全查看
计数器存在 ReentrantLock 内部的同步器(AbstractQueuedSynchronizer 子类)里,对外不可见。唯一公开的访问方式是 getHoldCount(),它返回当前线程对该锁的持有次数。
立即学习“Java免费学习笔记(深入)”;
-
getHoldCount()只对当前线程有效,其他线程调用返回 0 - 别用它做业务逻辑分支(比如“如果 holdCount > 2 就跳过某操作”),因为计数本身是锁的实现细节,不是语义契约
- 调试时可打印
lock.getHoldCount(),但上线后建议移除——它有轻微性能开销,且依赖内部状态 - 注意:即使没调用
lock(),getHoldCount()也安全,返回 0
unlock() 忘记调用或调用次数不匹配的后果
少调一次 unlock(),锁永远无法完全释放,其他线程会被永久阻塞;多调一次则抛 IllegalMonitorStateException,且不会回滚之前成功的解锁操作。
- 务必确保每个
lock()都对应一个且仅一个unlock(),推荐用try-finally包裹 - 不要在
finally块里写if (lock.isHeldByCurrentThread()) lock.unlock();——这是冗余的,unlock()自身已做线程校验 - 嵌套方法调用时,避免在子方法里擅自
unlock(),除非你明确设计为“移交锁所有权” - 使用
LockSupport.park()或条件变量(Condition)时,await()会自动释放锁,signal()后需重新竞争,这和重入计数无关,但容易混淆释放时机
重入本身很可靠,真正难的是厘清“谁该在哪儿加锁、释放”,尤其是跨方法、跨回调、跨线程边界时。一旦锁的生命周期脱离了单一线程栈帧,重入机制就帮不上忙了。









