轻量级锁在自旋失败达阈值或检测到多线程争抢时立即膨胀为重量级锁,引发CPU骤降、线程批量阻塞、上下文切换飙升及响应延迟尖峰,性能呈断崖式下降。

轻量级锁本身不设计用于高并发场景,它的膨胀时机直接决定系统是“平稳运行”还是“突然卡顿”。一旦触发升级,性能不是缓慢下降,而是出现断崖式变化。
轻量级锁什么时候会膨胀
膨胀不是按线程数量线性发生的,而是由两个关键动作触发:
- 自旋失败达到阈值:JDK 默认最多自旋10次(可通过-XX:PreBlockSpin调整),每次失败都重试CAS;一旦全部失败,立刻放弃自旋
- 检测到多线程同时争抢:比如3个及以上线程在同一毫秒级时间窗口内尝试获取同一把锁,JVM会跳过自旋直接升级,避免CPU空转浪费
膨胀瞬间的典型性能冲击
从轻量级锁升为重量级锁那一刻,系统行为发生质变:
- CPU使用率可能骤降——原本多个线程在用户态自旋抢锁,现在全被挂起,不再消耗CPU,但吞吐量同步崩塌
- 线程状态批量切换:RUNNABLE → BLOCKED,jstack里能看到大量线程停在
monitorenter,而非之前分散的自旋循环 - 上下文切换飙升:每个阻塞/唤醒都涉及用户态到内核态切换,单次耗时几百纳秒起,高并发下累计开销巨大
- 响应延迟尖峰:持有锁的线程若恰逢GC、IO等待或调度延迟,等待队列里的线程只能干等,P99延迟可能翻倍甚至更高
如何判断已经踩中膨胀拐点
不能只看线程数或QPS,要结合运行时信号交叉验证:
立即学习“Java免费学习笔记(深入)”;
- 启用-XX:+PrintSafepointStatistics:重量级锁膨胀需进入全局安全点,safepoint停顿会明显突增
- 用async-profiler采样热点:如果
ObjectMonitor::enter调用占比显著上升,说明已走重量级路径 - 观察jstat -compiler输出:频繁锁膨胀虽不直接显示,但配合jstack发现大量BLOCKED线程,基本可确认
- 禁用偏向锁后压测对比:若关闭-XX:-UseBiasedLocking后轻量级锁膨胀更早发生,说明原偏向撤销过程已加剧竞争
实际影响比想象中更隐蔽
轻量级锁退化不只发生在极端峰值,日常流量波动也可能触碰边界:
- 缓存击穿时,大量请求涌向同一热点对象(如商品详情页),哪怕只有20–30线程,若集中在几毫秒内到达,仍会快速膨胀
- 虚假共享(false sharing)会放大问题:多个线程自旋修改同一缓存行里的Mark Word,导致CPU缓存频繁失效,加速自旋失败
- 锁粒度不合理时,轻量级锁反而成瓶颈——例如用一个全局锁保护高频更新的计数器,比直接换
AtomicInteger慢得多



















