可重入锁通过“记录持有者+计数器”机制实现,即AQS中state字段记录重入次数、exclusiveOwnerThread保存持有线程;同一线程重复加锁仅增state,unlock()减至0才真正释放,从而避免自死锁。

可重入锁的核心在于“记录持有者+计数器”机制,不是靠检测死锁来避免阻塞,而是让线程在已持锁的前提下,直接增加计数并放行。
锁内部维护线程标识和重入次数
以 ReentrantLock 为例,它底层使用 AQS(AbstractQueuedSynchronizer)实现。AQS 的同步状态(state)字段不仅表示锁是否被占用,还记录当前持有线程的重入次数。同时,它会保存当前成功获取锁的线程引用(exclusiveOwnerThread)。
- 首次获取锁:state 设为 1,exclusiveOwnerThread 记录当前线程
- 同一线程再次 lock():state 加 1(如变为 2),不阻塞,直接返回
- 每次 unlock():state 减 1,仅当 state 变为 0 时才真正释放锁,并清空 exclusiveOwnerThread
可重入性解决了递归调用或嵌套加锁场景
没有可重入性时,一个已持锁的线程再次请求同一把锁就会被自己阻塞——这本质是死锁(自旋等待自身释放)。可重入锁通过“认得自己”来规避:
- 方法 A 加锁后调用方法 B,B 内部也尝试 lock() → 允许进入
- synchronized 也是可重入的:同一个线程进入多个 synchronized 方法或代码块不会卡住
- 若换成非可重入锁(如早期的 ReentrantLock 非公平模式误配、或自定义不可重入锁),就会无限等待
注意:可重入 ≠ 可以跨线程共享锁
可重入只对“同一个线程”生效。不同线程仍需排队竞争,且不能借用其他线程的锁计数。例如:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 线程 T1 持有锁(state=2),T2 尝试 lock() → T2 被阻塞,直到 state 归零
- T1 调用 unlock() 两次后 state=0,锁才真正释放,T2 才有机会获取
- 不可靠地“替别人 unlock()”会导致 IllegalMonitorStateException 或状态错乱
手动实现简易可重入逻辑(便于理解)
可用 ThreadLocal + 计数模拟核心思想(仅示意,不替代真实锁):
<font size="2">private final ThreadLocal<Integer> holdCount = ThreadLocal.withInitial(() -> 0);<br>private volatile Thread owner;<br><br>public void lock() {<br> Thread current = Thread.currentThread();<br> if (owner == current) {<br> holdCount.set(holdCount.get() + 1); // 同线程,只增计数<br> return;<br> }<br> // 否则尝试抢占(可能阻塞)...<br> acquire();<br> owner = current;<br> holdCount.set(1);<br>}</font>
关键点在于:判断当前线程是否已是 owner,是则跳过竞争流程,只更新计数。

















