锁升级是jvm根据竞争自动触发的状态迁移,核心在于mark word变化:无锁(01+哈希码)、偏向锁(01+线程id)、轻量级锁(00+lock record指针)、重量级锁(10+monitor指针);首次竞争撤销偏向锁,自旋失败后轻量级锁升级为重量级锁,且不可逆。

锁升级不是人为控制的流程,而是JVM根据实际竞争情况自动触发的状态迁移。看穿它的关键,在于理解对象头中Mark Word的变化、线程行为对锁状态的扰动,以及每一步升级背后的现实动因。
盯住对象头:Mark Word是锁状态的“仪表盘”
每个Java对象的对象头里,Mark Word字段实时反映当前锁所处的状态。它不存“锁本身”,只存锁的“身份标识”:
- 无锁时:Mark Word主要存哈希码和GC年龄,锁标志位为01
- 偏向锁时:存持有线程ID + 偏向标志,锁标志位仍为01(靠高位区分)
- 轻量级锁时:存指向线程栈中Lock Record的指针,锁标志位变为00
- 重量级锁时:存指向Monitor对象的指针,锁标志位变为10
只要能观察到Mark Word内容变化(例如用JOL工具或调试器查看),就能直接确认当前锁处于哪一阶段,无需猜测。
第一次竞争就是分水岭:偏向锁如何“破防”
偏向锁只在“零竞争”下成立。一旦第二个线程尝试获取同一把锁,JVM就必须处理冲突:
- 原持有线程仍在执行?JVM会暂停它(安全点),清空偏向状态,将锁升级为轻量级锁
- 原持有线程已释放锁?JVM直接撤销偏向,进入无锁状态,再由新线程竞争轻量级锁
- 这个过程叫“偏向锁撤销”,本质是一次同步开销,但只发生一次——之后就不再走偏向路径
所以,只要代码里出现两个不同线程交替或并发进入同一个synchronized块,偏向锁必然失效并升级。
自旋失败才升级:轻量级锁不是“过渡摆设”
轻量级锁的核心是CAS+自旋,它不阻塞线程,但也不无限等待:
- 线程尝试用CAS将Mark Word从“无锁/偏向”更新为自己栈中的Lock Record指针
- 若成功,获得锁;若失败(别人已占),就进入自旋循环,反复尝试CAS
- 自旋次数达到阈值(默认约10次,可由-XX:PreBlockSpin调整)仍未成功,就膨胀为重量级锁
这意味着:轻量级锁是否升级,取决于“竞争持续时间”而非“竞争线程数”。短时争抢走轻量级锁,长时争抢才进重量级。
重量级锁是兜底方案:一旦进去就不再回头
重量级锁启用操作系统Mutex,线程挂起与唤醒开销大,但能彻底解决高并发排队问题:
- 锁状态变成10,Mark Word指向堆里的Monitor对象
- 后续所有线程获取该锁,都必须进入Monitor的EntryList排队,走内核态阻塞
- 锁升级不可逆——哪怕之后只剩一个线程访问,它依然走重量级路径
这也是为什么避免重量级锁成为性能调优重点:不是因为它“错”,而是因为它的成本远高于前两级,且无法降级回收。











