重量级锁是synchronized在高竞争下自动升级的最终状态,触发条件为轻量级锁自旋失败达阈值(默认10次)或耗时过长,此时jvm将mark word锁标志位由00改为10,写入objectmonitor指针,线程挂起进入内核态等待,开销大但保证强一致性,且不可降级。

当多线程竞争激烈时,synchronized 会从轻量级锁升级为重量级锁——这不是手动控制的过程,而是 JVM 在运行时自动触发的不可逆膨胀行为。
轻量级锁升级为重量级锁的触发条件
核心判断依据是:自旋失败达到阈值。JVM 不会无限自旋,因为持续空转浪费 CPU。一旦线程在用户态尝试获取锁失败次数过多(默认 10 次,可通过 -XX:PreBlockSpin 调整),或自旋耗时过长(例如超过一定纳秒数),就会放弃自旋,进入阻塞队列。
- 自旋期间,线程始终在用户态运行,不切换到内核态
- 自旋失败后,JVM 将对象头 Mark Word 的锁标志位由
00(轻量级锁)改为10(重量级锁) - 同时在对象头中写入指向
ObjectMonitor的指针,该 Monitor 位于堆内存中,由操作系统互斥量(Mutex)支撑 - 当前线程被挂起,进入内核态等待,交由 OS 调度唤醒
升级后的关键变化
重量级锁意味着同步开销显著上升,但换来的是强一致性和公平性保障:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 锁的持有者仍通过
monitorenter指令进入,但底层已绑定到ObjectMonitor - 竞争线程不再自旋,而是进入
EntryList等待队列,被 OS 线程调度器挂起(park) - 释放锁时,JVM 唤醒
EntryList中的一个线程(非公平策略,默认不保证 FIFO) - 每次锁获取/释放都涉及用户态 → 内核态切换,带来上下文保存与恢复开销
为什么不能降级
重量级锁一旦形成,就不会再变回轻量级或偏向锁:
- 降级需要安全点(Safepoint)暂停所有线程,成本远高于维持当前状态
- JVM 设计哲学是“宁可多花一点内存和内核开销,也不引入复杂降级逻辑”
- 实际业务中,一旦出现重度竞争,往往持续存在,降级收益极小
如何观察是否升到了重量级锁
可通过 JVM 参数辅助验证:
- 加
-XX:+PrintGCDetails -XX:+PrintStringDeduplicationStatistics不够,需用-XX:+UnlockDiagnosticVMOptions -XX:+PrintBiasedLockingStatistics查看偏向锁撤销统计 - 更直接的方式是使用
jstack <pid></pid>查看线程堆栈:若看到大量线程处于BLOCKED (on object monitor)状态,并指向同一对象,基本确认已膨胀为重量级锁 - 配合 JFR(Java Flight Recorder)录制锁事件,可精确捕获
jdk.JavaMonitorEnter和阻塞时长
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










