synchronized锁升级本身不直接导致停顿,但偏向锁撤销和轻量级锁膨胀为重量级锁会引发短暂STW或高并发调度压力。前者需全局安全点暂停所有线程,后者涉及Monitor初始化与内核态切换。

synchronized 锁升级本身不会直接造成程序“停顿”,但锁状态切换过程中的特定操作,尤其是偏向锁撤销和轻量级锁膨胀为重量级锁这两个环节,会引发短暂的全局安全点(safepoint)暂停,表现为用户可感知的卡顿。
偏向锁撤销触发 STW(Stop-The-World)
当一个已偏向的锁被其他线程竞争时,JVM 必须先撤销该偏向状态,才能升级为轻量级锁。而撤销操作需要确保目标线程不处于临界区内——这只能在所有 Java 线程都到达安全点后执行。此时 JVM 会强制挂起所有应用线程,完成撤销后再恢复运行。
- 典型场景:启动后大量线程首次争抢同一个对象锁(如单例初始化后的高频访问)
- 影响范围:整个 JVM 进程,不只是当前对象
- 可观测现象:GC 日志中出现 "BiasedLocking::revoke_and_rebias" 或 "RevokeBias" 记录,同时伴随 safepoint 日志
轻量级锁自旋失败后膨胀为重量级锁
多个线程通过 CAS 尝试获取轻量级锁失败后,若自旋次数耗尽(默认10次),JVM 会将锁升级为重量级锁。该过程需将 Monitor 对象从栈上移到堆中,并关联操作系统互斥量。
- 这不是 STW 操作,但涉及对象头修改、Monitor 初始化及内核态资源申请
- 单次开销小,但在高并发密集争用下,大量线程几乎同时触发膨胀,会造成瞬时 CPU 和调度压力上升
- 线程进入阻塞队列前的最后一步,可能叠加上下文切换延迟
Monitor 初始化与竞争队列构建
重量级锁依赖 ObjectMonitor 结构,首次构建 Monitor 时需分配内存并初始化等待队列。虽然 Monitor 可复用,但初始创建发生在第一次锁膨胀时。
立即学习“Java免费学习笔记(深入)”;
- JVM 内部需原子地更新对象头指向新 Monitor 地址
- 若此时有其他线程正尝试 CAS 修改对象头,可能产生短暂重试延迟
- Monitor 的 EntryList/Cxq 队列操作虽在用户态完成,但后续 park/unpark 调用会切入内核态
如何定位是否由锁升级导致停顿
启用 JVM 参数组合可捕获关键线索:
- -XX:+PrintBiasedLockingStatistics:输出偏向锁启用/撤销统计
- -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(配合 hsdis):查看锁相关汇编指令变化
- -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime:观察是否有非 GC 引起的停顿记录
- jstack 输出中大量线程处于 BLOCKED 状态,且堆栈集中在同一 synchronized 方法或代码块


















