synchronized锁升级是jvm根据实际竞争动态触发的单向过程,仅在多线程真实争抢同一锁时,才从偏向锁→轻量级锁→重量级锁逐步演进;单线程或低并发下不显现,需通过jvm参数、竞争阶梯设计及jstack/jol等工具验证状态变化。

synchronized 锁升级在多线程测试中不是“固定发生”的事件,而是 JVM 根据实际竞争情况动态触发的运行时行为。它不会在单线程或低并发场景下显现,只有当多个线程真实争抢同一把锁时,才会逐步从偏向锁 → 轻量级锁 → 重量级锁演进。理解这点,才能设计出能暴露锁升级现象的有效测试。
测试前需确认 JVM 参数和对象状态
默认情况下(JDK 8+),偏向锁是开启的,但会在启动后约 4 秒延迟启用(可通过 -XX:BiasedLockingStartupDelay=0 立即生效)。若测试时间太短或对象刚创建就进入同步块,可能直接跳过偏向阶段。建议显式关闭偏向锁(-XX:-UseBiasedLocking)来观察轻量级锁与重量级锁的切换,或保留偏向锁并确保:① 同一对象被同一个线程多次进入同步块(触发偏向);② 随后由另一个线程尝试获取该锁(触发撤销与升级)。
用代码构造可观察的竞争阶梯
- 创建一个共享对象(如 new Object() 或普通 POJO),所有线程都对其加锁
- 先让线程 A 连续执行 10 次 synchronized 块(建立偏向锁)
- 再启动线程 B、C 同时争抢该锁(触发偏向锁撤销 → 升级为轻量级锁)
- 增加线程数(如 20+ 线程高频率争抢),或在 synchronized 块内加入毫秒级休眠(模拟长临界区),促使自旋失败 → 升级为重量级锁
如何验证锁是否升级
仅靠业务逻辑输出无法判断锁状态。可靠方式是结合 JVM 工具:
- 添加参数:-XX:+PrintGCDetails -XX:+PrintGCSummary -XX:+UnlockDiagnosticVMOptions -XX:+PrintBiasedLockingStatistics,运行后查看控制台是否打印偏向锁撤销次数、轻量级锁膨胀次数等统计
- 使用 jstack
查看线程堆栈,若看到大量线程处于 BLOCKED (on object monitor) 状态,说明已进入重量级锁(OS 层阻塞);若只有少量线程在自旋等待(无 BLOCKED),更可能是轻量级锁阶段 - 通过 JOL(Java Object Layout)库打印对象头:System.out.println(ClassLayout.parseInstance(obj).toPrintable());,观察 mark word 中的锁标志位(01=偏向,00=轻量,10=重量)
常见误判与干扰因素
测试中容易把现象归因错误:
- 吞吐量下降 ≠ 一定升级到重量级锁:轻量级锁下大量自旋也会吃 CPU,导致性能跌,此时 jstack 仍显示 RUNNABLE,而非 BLOCKED
- 首次同步不慢,不代表没锁开销:偏向锁建立本身有 CAS 开销,只是后续重入极快;而重量级锁的唤醒/调度代价才真正拖慢响应
-
静态方法锁与实例方法锁互不影响:
synchronized static锁的是 Class 对象,synchronized实例方法锁的是 this,两者锁对象不同,不会触发同一次升级过程
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











