synchronized 底层原理
synchronized 是 Java 中最基础的同步工具,其底层实现涉及字节码、JVM 内部结构以及硬件指令。理解它对于编写高效的并发代码至关重要。
字节码层面:monitorenter 和 monitorexit
编译后的 .class 文件中,synchronized 会转换为特定的机器指令。
同步代码块
对于 synchronized(obj) { ... },编译器会在前后生成 monitorenter 和 monitorexit 指令。
public void method() {
synchronized(obj) {
System.out.println("hello");
}
}
反编译后的字节码大致如下:
public void method();
Code:
0: aload_0
1: getfield #2 // Field obj:Lcom/example/Obj;
4: dup
5: astore_1
6: monitorenter // 进入同步块,尝试获取锁
7: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #4 // String hello
12: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: aload_1
16: monitorexit // 正常退出同步块,释放锁
17: goto 25
20: astore_2
21: aload_1
22: monitorexit // 异常退出同步块,释放锁
23: aload_2
24: athrow
25: return
注意这里有两个 monitorexit。第一个对应正常流程,第二个隐藏在 finally 语义中,确保即使代码抛出异常,锁也能被正确释放。
同步方法
对于 synchronized 修饰的方法,常量池中会设置 ACC_SYNCHRONIZED 标志。调用时虚拟机检查该标志,自动处理加锁和解锁逻辑,无需手动控制。
JVM 底层实现:对象头与 Monitor
monitorenter 指令的核心在于操作 Java 对象头 和 Monitor。
1. Java 对象头(Mark Word)
在 HotSpot 虚拟机中,对象内存布局包含对象头、实例数据和对齐填充。对象头中的 Mark Word 存储了运行时数据,如哈希码、GC 分代年龄、锁状态标志 等。它是锁机制的主战场。
Mark Word 是非固定动态数据结构,会根据对象状态复用空间。在 32 位 JVM 下,最后 2 位标识锁状态,锁升级过程就体现在这 2 位的变化上。





