锁升级过程:无锁→偏向→轻量→重量
synchronized 不是一开始就使用重量级锁,而是会根据竞争情况动态升级。这个过程叫做「锁升级」或「锁膨胀」,是 JDK 6 引入的重要优化。
锁升级总览
无锁状态
对象刚创建时,处于无锁状态:
无锁状态特点
- Mark Word 包含对象的 hashCode(如果已计算)
- 分代年龄
- 无锁标志
偏向锁
原理
当一个线程首次获取锁时,如果启用偏向锁(默认启用),会将线程 ID 记录在对象头的 Mark Word 中:
偏向锁的优势
偏向锁的撤销
在以下情况会撤销偏向锁:
- 其他线程尝试获取锁:当有其他线程尝试获取已被偏向的锁时,JVM 会撤销偏向锁
- 调用 hashCode():调用
Object.hashCode()或System.identityHashCode()会撤销偏向 - 调用 wait()/notify():调用
wait()或notify()会撤销偏向
批量偏向与撤销
JVM 维护了一个「偏向批量阈值」:
轻量级锁
原理
当偏向锁撤销或多个线程交替获取锁时,升级为轻量级锁:
轻量级锁的获取
自旋优化
轻量级锁获取失败后,不会立即升级为重量级锁,而是先自旋重试:
自旋次数自适应
JDK 6 之后,自旋次数不再固定,而是根据之前的自旋成功率动态调整:
重量级锁
原理
当自旋超过阈值或竞争激烈时,升级为重量级锁:
Monitor 的工作原理
重量级锁的开销
重量级锁的主要开销来自用户态到内核态的切换:
锁升级的触发条件
总结
竞争场景分析
实战:观察锁升级
j OL 工具
输出示例
性能调优建议
禁用偏向锁
如果没有竞争,或竞争很少,可以禁用偏向锁:
控制自旋次数
逃逸分析
如果锁对象不逃逸出方法,可以消除锁:
本章总结
核心要点:
- 锁升级是单向的:偏向锁 → 轻量级锁 → 重量级锁
- 偏向锁:消除同一线程的 CAS 开销
- 轻量级锁:使用 CAS + 自旋避免线程阻塞
- 重量级锁:OS Mutex,开销大
- 触发条件:根据竞争情况自动升级
- 调优策略:禁用偏向锁、逃逸分析
理解锁升级过程是深入 Java 并发性能优化的基础。下一节我们将讲解 AQS 框架。