synchronized 底层原理(总结版)
synchronized 底层使用的是 monitor,Monitor 被翻译为监视器,是由 JVM 提供,C++ 语言实现。
使用 javap -v xxx.class 反编译一段代码可以看到机器指令:
- monitorenter:上锁开始的地方
- monitorexit:解锁的地方
- 其中被 monitorenter 和 monitorexit 包围住的指令就是上锁的代码
- 第二个 monitorexit 是为了防止锁住的代码抛异常后不能及时释放锁
Monitor 主要就是跟这个对象产生关联,如下图:

Monitor 内部具体的存储结构:
- Owner:存储当前获取锁的线程,只能有一个线程可以获取
- EntryList:关联没有抢到锁的线程,处于 Blocked 状态的线程
- WaitSet:关联调用了 wait 方法的线程,处于 Waiting 状态的线程
具体的流程:
- 进入 synchronized 代码块时,先让 lock(对象锁)关联 monitor,然后判断 Owner 是否有线程持有
- 如果没有线程持有,则让当前线程持有,表示该线程获取锁成功
- 如果有线程持有,则让当前线程进入 entryList 进行阻塞,如果 Owner 持有的线程已经释放了锁,在 EntryList 中的线程去竞争锁的持有权(非公平)
- 如果代码块中调用了 wait() 方法,则会进去 WaitSet 中进行等待
synchronized 底层原理(详解版)
synchronized 的底层原理可以从三个层面来看:字节码层面、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 // 获取对象引用 obj
4: dup
5: astore_1
6: monitorenter // 进入同步块,尝试获取锁
7: getstatic #3 // 获取 System.out
10: ldc #4 // 加载 "hello"
12: invokevirtual #5 // 调用 println
15: aload_1
16: monitorexit // 正常退出同步块,释放锁
17: goto 25
20: astore_2
21: aload_1
22: monitorexit // 异常退出同步块,释放锁 (确保在异常情况下也能释放锁)
23: aload_2
24: athrow
25: return


