synchronized锁膨胀是JVM根据线程竞争自动触发的渐进式优化:无锁→偏向锁→轻量级锁→重量级锁,全程不可逆且无需人工干预。

synchronized 的锁膨胀不是人为控制的流程,而是 JVM 根据实际线程竞争情况自动触发的状态演进。它本质是一套渐进式优化策略:能不加锁就不加锁,能轻量就不重量,只有真出现高竞争才走代价最高的路径。
无锁状态是起点,不是“没锁”而是“还没被锁住”
对象刚创建时,Mark Word 存储哈希码、分代年龄等信息,锁标志位为 01,表示当前无任何线程持有锁。此时没有任何同步开销,纯粹是对象的初始状态。只要没线程执行 synchronized 块,就一直维持这个状态。
偏向锁解决单线程反复进入的开销
当第一个线程首次进入同步块,JVM 在安全点将对象头 Mark Word 中的线程 ID 设为该线程,并把偏向标志位置为 1(仍是 01,但含义变了)。后续该线程再次进入,只需比对线程 ID 是否一致,无需 CAS 或系统调用——相当于“免检通道”。
- 触发条件:仅一个线程访问,且未发生竞争
- 撤销时机:其他线程尝试获取锁时,需等待全局安全点,然后撤销偏向并升级
- 注意:已计算过 identityHashCode 的对象无法启用偏向锁(Mark Word 空间被占用);JDK 15+ 默认关闭偏向锁,因现代应用多线程场景更普遍
轻量级锁靠自旋避免内核态切换
偏向锁被撤销后,或多个线程交替访问时,JVM 将锁升级为轻量级锁。此时线程在自己的栈帧中创建 Lock Record,通过 CAS 尝试把 Mark Word 替换为指向该记录的指针(标志位变为 00)。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 成功:线程获得锁,直接执行
- 失败:说明有竞争,线程进入自旋(空转等待),默认最多尝试 10 次(JDK 后期支持自适应调整)
- 自旋意义:避免立即陷入阻塞——若持有锁的线程很快释放,自旋比挂起/唤醒更省资源
重量级锁是最后防线,依赖操作系统互斥量
当自旋失败次数超限,或竞争线程数较多(如超过 CPU 核数一半),JVM 就将锁膨胀为重量级锁。Mark Word 被替换为指向 C++ 实现的 ObjectMonitor 对象的指针(标志位变为 10)。
- 此时未获取锁的线程会被挂起,进入 Monitor 的 EntryList 或 WaitSet,由操作系统调度唤醒
- 代价在于用户态 ↔ 内核态切换,上下文保存与恢复带来显著开销
- 一旦升级为重量级锁,就不会再降级回轻量或偏向——不可逆是设计前提
整个过程没有代码干预,全由 JVM 运行时动态决策。真正影响性能的,不是“用了 synchronized”,而是“在什么竞争强度下走了哪条路径”。压测时发现 QPS 骤降,往往不是锁本身的问题,而是轻量级锁频繁自旋耗尽 CPU,或已悄然升级为重量级锁导致线程排队阻塞。

















