synchronized 底层原理(JVM 指令与 monitor 锁)¶
一、两种用法¶
// 1. 修饰方法
public synchronized void method() {}
// 2. 修饰代码块
public void block() {
synchronized (lock) { ... }
}
二、代码块的字节码¶
编译后:
aload_0
dup
astore_1
monitorenter ; 进入锁
getstatic ...
ldc "hi"
invokevirtual println
aload_1
monitorexit ; 正常退出
goto goto end
athrow
aload_1
monitorexit ; 异常退出
end:
return
三、monitor 对象¶
每个 Java 对象都关联一个 monitor(管程):
monitorenter:计数器 +1,为 0 时获取锁。monitorexit:计数器 -1,为 0 时释放。
monitor 存在对象头(Mark Word)里。
四、对象头¶
对象头分两部分:
Mark Word 存: - hashCode - GC 分代年龄 - 锁状态标志(无锁/偏向/轻量级/重量级) - 锁指针(指向 monitor)
五、锁升级过程¶
JDK 6 引入锁优化,锁从低到高:
- 无锁:刚创建对象。
- 偏向锁:第一个线程访问,Mark Word 记线程 ID,以后这个线程进入不用同步。
- 轻量级锁:有竞争,CAS 自旋。
- 重量级锁:自旋失败,升级为 OS 互斥量,线程阻塞。
锁只能升级不能降级。
六、修饰方法的字节码¶
方法级 synchronized 不通过 monitorenter/monitorexit,而是方法标志位 ACC_SYNCHRONIZED,JVM 调用方法时检查这个标志,自动加锁。
高频追问
- synchronized 锁的是对象,不是代码块。
- 普通方法锁 this,静态方法锁 Class 对象。
- 偏向锁在 JDK 15 后默认关闭(因为现代应用多线程竞争多,偏向锁收益小)。