synchronized靠锁对象内部的\_owner和\_recursioncount字段实现可重入:线程首次获取锁时设置\_owner并置\_recursioncount为1,再次进入时仅递增计数,退出时递减,归零才释放锁。

同一个线程能多次进入 synchronized 保护的代码,靠的不是“开绿灯”,而是锁对象内部一个自动维护的计数器。它不看调用次数,只认线程身份和当前计数。
锁对象里藏着两个关键字段
每个 Java 对象的监视器(Monitor)在 JVM 中实际包含两个核心状态:
- _Owner:记录当前持有锁的线程 ID,空表示无人持锁
- _RecursionCount:记录该线程对这把锁的获取次数,初始为 0
线程每次进入同步块或同步方法时,JVM 都会检查 _Owner 是否等于自己。匹配就只更新计数,不阻塞;不匹配才排队等待。
一次典型重入过程是这样走的
以递归调用为例,假设线程 T 调用一个 synchronized 方法,又在里面调用另一个 synchronized 方法:
- T 第一次进锁 → _Owner 设为 T,_RecursionCount 变成 1
- T 在方法内再次进同一把锁(比如调用另一个 synchronized 实例方法)→ _Owner 已是 T,直接 _RecursionCount 加到 2
- 内层方法退出 → _RecursionCount 减为 1
- 外层方法退出 → _RecursionCount 减为 0,此时 _Owner 才清空,锁真正释放
必须是“同一把锁”,否则不生效
可重入性有严格前提,容易误用:
- 对实例方法加锁,锁的是
this;静态方法加锁,锁的是Class对象——两者互不干扰 - 线程持有
obj1的锁,再去拿obj2的锁,哪怕都是 synchronized,也不算重入,可能阻塞 - 不同线程竞争同一把锁时,可重入性完全不起作用,该排队还是排队
和 ReentrantLock 对比,看清设计差异
两者都靠“线程 ID + 计数”实现可重入,但暴露程度不同:
-
synchronized完全隐藏计数逻辑,你无法读取或干预 _RecursionCount -
ReentrantLock提供getHoldCount(),运行时能查到当前持锁层数,适合调试漏 unlock 的问题 -
ReentrantLock支持公平模式,但公平锁下重入仍要排队;synchronized始终让持有者优先继续执行










