Java synchronized 关键字详解:从字节码到对象头与锁升级
synchronized 是 Java 中最基础的同步机制,其底层实现涉及字节码指令、JVM 对象头结构以及操作系统的互斥锁。理解这些细节对于编写高性能并发代码至关重要。
1. 字节码层面:monitorenter 和 monitorexit
当编译器处理 synchronized 关键字时,会在字节码中生成对应的指令。无论是修饰代码块还是方法,核心逻辑都围绕 monitorenter 和 monitorexit 展开。
同步代码块
对于 synchronized(object) { ... } 这种形式,编译器会在同步块前后分别插入 monitorenter 和 monitorexit 指令。注意,这里会有两个 monitorexit,第二个是为了确保在发生异常时锁也能被正确释放(类似 finally 语义)。
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 指令,第一个用于正常退出,第二个用于处理异常情况,这确保了即使同步块内抛出异常,锁也能被正确释放。
同步方法
对于 synchronized 修饰的方法,方法常量池中会设置 ACC_SYNCHRONIZED 标志。调用时,虚拟机检查该标志,若存在则先尝试获取锁(实例方法为 this,静态方法为 Class 对象),执行完方法体后自动释放锁。
2. JVM 底层实现:对象头与 Monitor
monitorenter 指令背后的具体实现,关键在于 Java 对象头 和 Monitor。
2.1 Java 对象头(Mark Word)
在 HotSpot 虚拟机中,每个 Java 对象的内存布局包含三部分:对象头、实例数据、对齐填充。其中对象头是理解锁的核心,它包含两部分信息:
- Mark Word:存储对象自身的运行时数据,如哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID 等。它是实现锁的'主战场'。




