synchronized同步块由jvm通过monitorenter和monitorexit指令驱动objectmonitor实现加锁解锁;enter按偏向锁、轻量级锁、重量级锁逐级升级,exit校验所有权并唤醒等待线程;锁对象仅为monitor载体,其引用须用private final保证稳定。

synchronized 同步块在并发编程中不是靠语言魔法实现的,而是由 JVM 通过 monitorenter 和 monitorexit 两条字节码指令,驱动底层 C++ 实现的 ObjectMonitor 完成加锁与解锁。理解它,关键在于看清“谁在操作谁”以及“什么时候操作”。
同步块的字节码是理解入口
写一个典型同步块:
public void doWork() {
synchronized (lock) {
// 业务逻辑
}
}
编译后用 javap -v 查看,你会看到类似结构:
-
monitorenter出现在synchronized大括号开始前(比如第6行) -
monitorexit出现在正常执行完大括号后(比如第16行) -
还有一条
monitorexit出现在异常处理路径里(比如第21–22行),确保抛异常时也能释放锁
这说明:JVM 把同步块“翻译”成一对有保障的 Enter/Exit 操作,且覆盖所有出口路径——这是 synchronized 不会死锁的根本原因。
Enter 指令触发 Monitor 的竞争流程
monitorenter 执行时,并不直接进入重量级阻塞,而是按锁状态分层尝试:
- 先检查对象头 Mark Word 是否为“无锁”或“偏向锁可撤销”,尝试用 CAS 替换为当前线程 ID(偏向锁)
- 失败则尝试把 Mark Word 改为指向栈中 Lock Record 的指针(轻量级锁)
- 再失败(比如存在多线程竞争),就膨胀为重量级锁:Mark Word 存入指向 ObjectMonitor 的指针,真正调用 Monitor 的
enter()方法
此时 ObjectMonitor 的 _owner 字段被设为当前线程,_EntryList 中等待的线程仍处于 BLOCKED 状态,尚未唤醒。
Exit 指令完成所有权交接
monitorexit 并非简单清空 _owner,而是完整参与 Monitor 协议:
- 校验当前线程是否真为
_owner,否则抛IllegalMonitorStateException - 清空
_owner,然后从_EntryList中随机唤醒一个线程(非公平策略) - 被唤醒线程重新尝试获取
_owner,若成功则继续执行;若失败,可能再次入队或自旋
注意:同一个 Monitor 对象必须被同一对 monitorenter/monitorexit 使用,否则校验失败——这也是为什么不能在不同同步块间“传递”锁对象引用。
锁对象本质是 Monitor 的绑定载体
很多人误以为 synchronized(obj) 是“锁住变量 obj”,其实:
-
obj只是提供一个堆中对象实例,JVM 通过它定位其关联的 ObjectMonitor(首次需要时才分配) - Monitor 不在 Java 堆里,而是在 JVM native 内存中;它不属于任何 Java 类,无法 new,也不受 GC 管理
- 如果
obj = new Object()被重赋值,新旧对象各自拥有独立 Monitor,锁完全无关
所以选锁对象时,务必用 private final 引用,避免因引用变更导致同步失效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











