Java中synchronized锁膨胀是单向升级过程:无锁→偏向锁→轻量级锁→重量级锁,不可逆;所谓“降级”仅指安全点时对偏向锁的批量撤销,并非重量级锁向低阶状态转换。

Java 中 synchronized 的“膨胀”是明确存在的:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,单向升级、不可逆。但所谓“重量级锁降级”,需谨慎看待——它并非运行时动态将重量级锁变回轻量级或偏向锁,而是在特定全局时机(如安全点)对锁状态做批量清理或重置,本质是撤销而非降级。
锁膨胀是刚性路径,没有运行时降级
从 HotSpot JVM 实现来看,一旦锁升级为重量级锁(Mark Word 锁标志位为 10),其对象头中存储的是指向操作系统互斥量(mutex)的指针。该状态无法在持有锁期间被“缩回”成轻量级锁,因为:
- 重量级锁涉及线程阻塞与唤醒,依赖 OS 级资源,JVM 无法在不破坏语义的前提下安全回收这些资源并重建轻量级锁结构;
- 轻量级锁依赖线程栈中的 Lock Record,而重量级锁下线程可能已挂起、栈帧不可达,无法复用;
- JVM 设计上未实现重量级锁到轻量级锁的转换逻辑,官方文档与源码均不支持此行为。
安全点处的“收缩”实为偏向锁批量撤销
所谓“在安全点降级”,实际仅发生在偏向锁层面,且只针对尚未升级为重量级锁的对象:
- 当 JVM 进入安全点(如 GC、JIT 编译、线程 dump 等),会扫描所有已偏向但未被竞争的对象;
- 若检测到某偏向锁所属线程已退出、或对象被大量线程访问过(通过 epoch 检查失效),则撤销该偏向锁,将其恢复为无锁状态(01);
- 这个过程常被误称为“降级”,但它不作用于重量级锁,也不产生轻量级锁,只是把“偏向锁”打回原形。
G1 GC 等场景下的强制重置
某些垃圾收集器(如 G1)在并发标记阶段,为避免偏向锁干扰对象图遍历,会触发全局偏向锁批量撤销(Bulk Revoke):
- 所有处于偏向状态的对象,无论是否活跃,都会被清除线程 ID,回归无锁状态;
- 后续若再次被同一线程加锁,可重新走偏向流程;若发生竞争,则直接跳过偏向,进入轻量级锁流程;
- 这仍是“清零+重启”,不是重量级锁向下转换。
为什么重量级锁不设计降级?
根本原因在于收益与风险不匹配:
- 重量级锁出现,说明竞争已真实发生且持续;即使短暂缓解,也无法预判后续是否再竞争,降级后很快又要升级,徒增开销;
- 降级需同步多个线程状态(如唤醒队列、等待线程栈)、校验锁重入计数等,实现复杂且易出错;
- JVM 选择更保守策略:让锁停留在最稳妥的状态,把优化重点放在避免升级(如关闭偏向锁、调高自旋阈值)上。

















