synchronized 是 Java 中最基础的同步机制,其底层实现涉及字节码指令、JVM 内存模型以及操作系统层面的互斥锁。理解它的工作原理,对于排查并发问题和优化性能至关重要。
1. 字节码层面:monitorenter 和 monitorexit
当编译器处理 synchronized 关键字时,会根据上下文生成特定的字节码指令。无论是修饰代码块还是方法,核心都在于锁的获取与释放。
同步代码块
对于 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
同步方法
对于 synchronized 修饰的方法,常量池中会设置 ACC_SYNCHRONIZED 标志。调用该方法的指令(如 invokevirtual)在执行前会检查此标志。若存在,线程需先尝试获取锁(实例方法为 this,静态方法为 Class 对象),执行完方法体后自动释放锁。
2. JVM 底层实现:对象头与 Monitor
monitorenter 和 monitorexit 的具体行为由 JVM 管理,核心依赖于 Java 对象头 和 Monitor。
2.1 Java 对象头(Mark Word)
在 HotSpot 虚拟机中,对象内存布局包含对象头、实例数据和对齐填充。其中对象头是关键,它包含两部分信息:
- Mark Word:存储对象运行时的元数据,如哈希码、GC 分代年龄、锁状态标志、偏向线程 ID 等。它是锁状态变化的'主战场'。
- Klass Pointer:指向类元数据的指针,确定对象类型。
Mark Word 是一个动态数据结构,根据对象状态复用存储空间。在 32 位 JVM 下,最后 2 位(lock)标识了对象的锁状态,锁的升级过程就体现在这 2 位的变化上。





