synchronized代码块通过monitorenter和monitorexit指令实现,而同步方法使用acc_synchronized标志;二者机制不同、不可互换,且acc_synchronized仅作用于方法级别,无法用于代码块。

Java 中 synchronized 代码块**不使用 ACC_SYNCHRONIZED 标志**,它用的是 monitorenter 和 monitorexit 字节码指令;而 ACC_SYNCHRONIZED 是同步方法(实例或静态)的底层标识。两者实现机制不同,不能混用或替代。
synchronized 代码块靠 monitorenter/monitorexit 指令
编译器在 synchronized(对象) 代码块的起始和结束位置,插入两条 JVM 指令:
- monitorenter:线程执行到此处时,尝试获取括号内对象关联的 Monitor 所有权。若 Monitor 未被占用,直接获得并把锁计数器设为 1;若已被其他线程持有,当前线程阻塞,进入该对象的 EntryList 等待队列。
- monitorexit:正常退出或异常退出时都会触发。每次执行都使锁计数器减 1;当计数器归零,Monitor 才真正释放,唤醒 EntryList 中等待的线程之一竞争所有权。
字节码中通常看到1 个 monitorenter + 2 个 monitorexit:一个对应正常流程结束,另一个对应异常处理路径(比如 try 块抛出 Exception),确保锁不会因异常而泄漏。
synchronized 方法靠 ACC_SYNCHRONIZED 标志
修饰方法时(如 public synchronized void m() 或 public static synchronized void m()),编译后方法的访问标志(flags)会设置 ACC_SYNCHRONIZED 位,但字节码中不出现 monitorenter/monitorexit 指令。
- JVM 在调用该方法前,自动检查这个标志;若存在,就要求线程先成功获取对应锁(实例方法锁 this,静态方法锁 Class 对象),再执行方法体。
- 方法返回(无论正常 return 还是异常抛出)时,JVM 自动释放锁——这个过程由虚拟机隐式完成,无需字节码显式控制。
Monitor 与对象头(Mark Word)是实际锁载体
无论是哪种语法形式,最终都依赖对象的 Monitor 和对象头中的 Mark Word 协同工作:
- 每个 Java 对象在创建时,JVM 都为其关联一个 Monitor(C++ 实现的 ObjectMonitor 结构)。
- Monitor 内部有 Owner(持有线程)、EntryList(阻塞等待线程)、WaitSet(wait() 挂起线程)等字段。
- 对象头的 Mark Word 动态记录锁状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁。当锁升级到重量级,Mark Word 就存指向该对象 Monitor 的指针。
为什么不能把 ACC_SYNCHRONIZED 用在代码块上
因为 ACC_SYNCHRONIZED 是方法级别的属性,定义在方法表(method_info)结构中,只对整个方法生效;而代码块是语句级结构,作用范围更细、位置更灵活,必须由指令级控制(monitorenter/monitorexit)才能精准包围目标代码段。JVM 规范不允许也不支持在代码块层面设置方法标志位。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











