synchronized是可重入锁,因其JVM监视器内置线程ID标识与递归计数器:同一线程重复获取同一对象锁时,仅计数器加1;退出时减1,归零才释放锁。

掌握 synchronized 的“可重入性”,关键不是记住定义,而是看清它怎么在 JVM 里一步步运行:同一个线程能再次进入已持有的锁,靠的不是特殊豁免,而是一套自动维护的计数机制。
可重入性的本质是“线程身份 + 计数器”
每个对象监视器(Monitor)内部有两个核心字段:_Owner 记录当前持锁线程 ID,_RecursionCount 记录该线程加锁次数。只要 _Owner 匹配当前线程,JVM 就不阻塞,只做计数增减。
- 第一次进 synchronized:_Owner 设为当前线程,_RecursionCount 变为 1
- 同一线程再次进同一把锁(比如递归调用或嵌套同步方法):_Owner 已匹配,直接 _RecursionCount +1(变成 2、3…)
- 每次退出同步块或方法:_RecursionCount -1;只有减到 0 时,_Owner 才清空,锁才真正释放
写代码时能观察到的典型场景
下面这种调用链不会卡死,正是可重入性在起作用:
public class Counter {
public synchronized void inc() {
System.out.println("inc start");
doWork(); // 再次进入同步方法
System.out.println("inc end");
}
public synchronized void doWork() {
System.out.println("doWork");
}
}
- 线程调用 inc() → 获取锁,_RecursionCount = 1
- inc() 中调用 doWork() → 发现 _Owner 是自己,_RecursionCount 变成 2
- doWork() 返回 → _RecursionCount 减为 1;inc() 返回 → 减为 0,锁释放
和 ReentrantLock 对比,加深理解
synchronized 的可重入是 JVM 自动完成的,你不用管计数;而 ReentrantLock 需要显式调用 lock() 和 unlock(),但它也依赖同样的逻辑:
- lock.isHeldByCurrentThread() 判断是否为自己持有
- lock.getHoldCount() 查看当前重入层数(比如返回 2,说明进了两层)
- 每次 unlock() 都只是减计数,不是立刻释放锁
容易误解的点
可重入 ≠ 任意线程都能重复拿锁,也不等于“锁失效”。它只对同一个线程生效,且必须是同一把锁对象(即同一个实例的 monitor):
- 线程 A 持有 obj1 的锁,再尝试获取 obj2 的锁 → 不可重入,可能阻塞
- 线程 A 和线程 B 同时调用 obj1 的 synchronized 方法 → 竞争锁,B 会被挂起,不受重入影响
- 静态 synchronized 方法锁的是 Class 对象,和实例方法锁的 this 不是同一把锁

















