synchronized 的字节码实现依赖 monitorenter 和 monitorexit 指令或 acc_synchronized 标志,通过对象头 mark word 控制 monitor 所有权,支持偏向锁、轻量级锁与重量级锁升级,并保证可重入性与异常安全的锁释放。

synchronized 的进入与退出在字节码层面是通过 monitorenter 和 monitorexit 指令实现的,而不是直接操作 Monitor 对象本身。JVM 在执行时,将这些指令映射到底层 Monitor 的所有权竞争逻辑上。
同步代码块:monitorenter + monitorexit 指令
当使用 synchronized(obj) { ... } 时,javac 编译后会在字节码中插入:
- 一个 monitorenter 指令 —— 出现在同步块入口处,表示“尝试获取 obj 关联的 Monitor”;
- 至少两个 monitorexit 指令 —— 一个用于正常流程退出,一个用于异常路径兜底释放锁(避免死锁)。
这种双 exit 设计确保无论是否抛出异常,锁都能被释放。你可以用 javap -v 查看 class 文件验证:常见结构是 1 个 monitorenter + 2 个 monitorexit。
同步方法:ACC_SYNCHRONIZED 标志位
当修饰实例方法时,字节码不会显式插入 monitorenter/monitorexit,而是在方法表中设置 ACC_SYNCHRONIZED 标志位。JVM 在调用该方法前自动执行 Monitor 获取逻辑(等价于在方法入口加 monitorenter),在方法返回(包括正常 return 或异常 throw)前自动执行 Monitor 释放(等价于插 monitorexit)。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 静态同步方法锁的是 Class 对象,对应 Class 的 Monitor;
- 实例同步方法锁的是 this,对应当前对象的 Monitor。
Monitor 的实际归属由对象头 Mark Word 控制
monitorenter 执行时,JVM 并不新建 Monitor,而是检查目标对象头中的 Mark Word:
- 若 Mark Word 中锁标志位为 01(无锁),则通过 CAS 尝试将线程 ID 写入,并升级为偏向锁或轻量级锁;
- 若已处于 重量级锁状态(标志位 10),则 Mark Word 存储着指向 ObjectMonitor 实例的指针,JVM 直接尝试抢占其 _owner 字段;
- 抢占失败时,线程被挂起并加入 Monitor 的 EntryList 队列,等待唤醒。
退出时触发 Monitor 状态变更与唤醒机制
monitorexit 执行时,JVM 做两件事:
- 将 Monitor 的重入计数器减一;若减至 0,则清空 _owner,并从 EntryList 中唤醒一个阻塞线程;
- 同时触发内存屏障(StoreLoad),确保本线程工作内存中对共享变量的修改已刷新到主内存,保障后续获得锁的线程能看见最新值。
这个过程天然支持可重入性:同一个线程多次进入同一把锁,只增加计数器,不改变 _owner。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










