synchronized 底层原理详解
synchronized 是 Java 并发编程中最基础也是最常用的同步机制。理解它的底层实现,对于掌握 JVM 内存模型、线程安全以及性能优化至关重要。我们可以从字节码、JVM 内部实现以及硬件层面三个维度来剖析。
1. 字节码层面:monitorenter 和 monitorexit
当编译器处理 synchronized 关键字时,无论是修饰代码块还是方法,都会生成对应的字节码指令。
同步代码块
对于 synchronized(object) { ... } 结构,编译器会在同步块前后分别插入 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
这里有两个关键点:
- 成对出现:
monitorenter负责加锁,monitorexit负责解锁。 - 异常保护:注意最后两个
monitorexit指令。第一个用于正常流程,第二个隐藏在finally语义中。这是为了确保即使同步块内抛出异常,锁也能被正确释放,避免死锁。
同步方法
对于 synchronized 修饰的方法,方法常量池中会设置 ACC_SYNCHRONIZED 标志。调用时(如 invokevirtual),JVM 会检查该标志,若存在则自动执行加锁逻辑。实例方法锁定的是 this 对象,静态方法锁定的是 Class 对象。
2. JVM 底层实现:对象头与 Monitor
monitorenter 指令背后的具体工作,依赖于 Java 对象头 和 Monitor(管程)。
2.1 Java 对象头(Mark Word)
在 HotSpot 虚拟机中,每个 Java 对象的内存布局包含对象头、实例数据和对齐填充。其中对象头是锁的核心载体,它包含两部分信息:
- Mark Word:存储对象运行时的元数据,如哈希码、GC 分代年龄、锁状态标志、偏向线程 ID 等。它是实现锁升级的关键。


