synchronized 实现原理与优化
synchronized 是 Java 最基础也是最重要的同步机制。从 JDK 5 到 JDK 21,它经历了多次重大优化,从最初的重量级锁到现在的轻量级锁,性能已经大幅提升。
synchronized 的基本用法
三种加锁方式
synchronized 的原理
字节码分析
Monitorenter / Monitorexit
对象头结构
synchronized 的锁信息存储在对象头中:
锁的优化
JDK 6 的锁优化
JDK 6 引入了一系列锁优化,使得 synchronized 的性能大幅提升:
锁粗化(Lock Coarsening)
连续的加锁操作合并为一次:
锁消除(Lock Elimination)
通过逃逸分析,如果锁对象不会逃逸出线程,则消除锁:
锁升级(Lock Escalation)
synchronized 会根据竞争情况自动升级:
偏向锁
原理
第一次获取锁时,将线程 ID 记录在对象头的 Mark Word 中:
偏向锁的获取
偏向锁的撤销
偏向锁在以下情况会撤销:
- 其他线程尝试获取锁
- 调用
Object.hashCode() - 调用
wait()/notify()
关闭偏向锁
如果确定没有竞争,可以关闭偏向锁提升性能:
轻量级锁
原理
使用 CAS 操作替代互斥量:
轻量级锁的获取
自旋优化
当轻量级锁获取失败时,先自旋重试:
重量级锁
原理
使用 OS 的互斥量(Mutex):
阻塞与唤醒
重量级锁的阻塞和唤醒涉及用户态到内核态的切换,开销较大:
性能对比
各锁状态的性能
最佳实践
减少锁粒度
减少锁持有时间
使用读写锁
本章总结
核心要点:
- synchronized 基于 Monitor 机制:锁信息存储在对象头中
- 锁优化:锁粗化、锁消除、锁升级
- 锁升级过程:偏向锁 → 轻量级锁 → 重量级锁
- 偏向锁:消除同一线程的 CAS 开销
- 轻量级锁:使用 CAS + 自旋避免线程阻塞
- 重量级锁:OS 互斥量,开销较大
下一节我们将详细讲解锁升级过程。