java中synchronized锁升级本质是jvm对mark word位模式的重解释,路径为无锁(01)→偏向锁(01+偏向位)→轻量级锁(00)→重量级锁(10),不可逆;低3位标识状态,其余位按状态存储哈希码、线程id、lock record或monitor指针。

Java 中 synchronized 锁升级过程中的锁标记(Mark Word)转换,本质是 JVM 对对象头中 Mark Word 的位模式重解释,而非真正“修改锁类型”。它发生在运行时,由 HotSpot 虚拟机根据竞争情况自动触发,整个过程不可逆(即升级后不会降级)。
锁标记在 Mark Word 中的存储结构
HotSpot 中,普通对象的 Mark Word(64 位,默认开启压缩指针)前 3 位(低三位)用于标识锁状态,其余位按不同锁状态存不同信息:
- 无锁状态(01):后 47 位存对象哈希码(未被调用过 hashCode),后 25 位存对象分代年龄(GC 相关)
- 偏向锁(01)+ 偏向标志位(第 3 位为 1):Mark Word 存储线程 ID、epoch、分代年龄等;此时锁标志位仍是 01,靠额外一位区分是否偏向
- 轻量级锁(00):Mark Word 存储指向栈中 Lock Record 的指针
- 重量级锁(10):Mark Word 存储指向 Monitor 对象(ObjectMonitor)的指针
- GC 标记(11):JVM GC 使用,与锁无关
锁升级的触发条件与转换路径
锁升级不是预设阶段切换,而是由实际同步行为驱动的被动调整:
- 新建对象默认为可偏向状态:Mark Word 锁标志=01,偏向标志=1,线程 ID 为空;首次加锁且无竞争时直接设置线程 ID,进入偏向锁
- 发生竞争时撤销偏向锁:另一线程尝试获取已偏向的锁 → 触发偏向撤销(revoke)→ 全局安全点暂停 → 将对象恢复为无锁状态(01)→ 然后直接膨胀为轻量级锁(00)
- 轻量级锁自旋失败后膨胀:多个线程同时竞争轻量级锁,自旋一定次数(-XX:PreBlockSpin,默认10次)仍无法获取 → JVM 将锁膨胀为重量级锁(10),Mark Word 指向堆中 ObjectMonitor
关键细节:偏向撤销与批量重偏向
偏向锁撤销开销较大(需进入安全点),因此 JVM 引入优化机制:
- 批量撤销:当某类对象频繁发生偏向撤销(如超过20次),JVM 会禁用该类所有新对象的偏向功能(-XX:BiasedLockingBulkRevokeThreshold)
- 批量重偏向:若检测到某类对象的偏向锁大量转移到新线程(如缓存淘汰场景),JVM 可在不进入安全点的情况下,将一批对象的偏向线程 ID 批量更新为目标线程(-XX:BiasedLockingBulkRebiasThreshold)
- 这些机制不影响 Mark Word 当前锁标记值,但改变后续加锁行为逻辑
如何观察锁状态变化
可通过 JVM 参数和工具验证锁标记转换:
- 启用详细锁日志:-XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions -XX:+PrintBiasedLockingStatistics
- 使用 jol(Java Object Layout)查看对象头:
ClassLayout.parseInstance(obj).toPrintable()输出中 “biased”、“thin”、“fat” 分别对应偏向、轻量、重量级锁 - JDK9+ 中,-XX:+TraceSynchronization 可打印锁操作跟踪(含膨胀、撤销事件)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











