java中不存在“偏向锁101、轻量锁000、重量锁010”的官方二进制编码,这只是民间为记忆锁升级特征而编的简化口诀,实际应关注三者行为本质:偏向锁面向单线程“懒加载信任”,轻量锁通过cas自旋应对短竞争,重量锁依赖操作系统mutex提供终极保障。

Java中并没有官方定义“偏向锁101、轻量锁000、重量锁010”这样的二进制暗号。这其实是对JVM锁升级状态的一种**民间简化类比**,用三位二进制数(如101)来形象记忆三种锁状态的特征组合,并非真实内存编码或JVM规范术语。
为什么会有“101/000/010”这种说法?
这是初学者在理解锁升级机制时,为方便记忆而提炼的**特征编码口诀**,每位代表一个关键属性:
- 第一位(百位):是否“偏向”某个线程 → 1 = 是(偏向锁),0 = 否
- 第二位(十位):是否涉及“CAS自旋”尝试 → 1 = 是(轻量锁会自旋),0 = 否(偏向锁无自旋,重量锁不自旋)
- 第三位(个位):是否依赖“操作系统互斥量(Mutex)” → 1 = 是(重量锁需挂起/唤醒线程,进系统态),0 = 否
于是:
- 偏向锁 ≈ 100(不是101)—— 偏向某线程(1)、无自旋(0)、不进系统态(0)
- 轻量锁 ≈ 010(不是000)—— 不偏向(0)、靠CAS自旋(1)、不进系统态(0)
- 重量锁 ≈ 011 或 001(不是010)—— 不偏向(0)、不自旋(0)、进系统态(1)
可见,网上流传的“101/000/010”并不严格对应逻辑,容易造成混淆。真正要记的是行为本质,不是硬背数字。
偏向锁:单线程场景的“懒加载信任”
它假设“锁只会被一个线程反复获取”,因此在第一次加锁时,直接把对象头Mark Word标记为“偏向该线程ID”,后续同一线程再进入无需同步操作——快得像没锁一样。
- 触发条件:新对象未被锁过,且JVM开启偏向锁(默认开启,但Java 15+已废弃)
- 撤销时机:有其他线程竞争 → 触发偏向撤销(会暂停应用线程,即“stop-the-world”小停顿)
- 注意:高竞争场景下,频繁撤销反而比不用偏向锁更慢
轻量锁:多线程短竞争的“自旋缓冲带”
当偏向锁被竞争打破,或对象从未偏向,JVM会尝试轻量锁:线程在栈帧中创建Lock Record,用CAS将对象头指向它;成功则持有锁,失败则自旋重试几次。
- 自旋目的:避免立即陷入内核态挂起,节省上下文切换开销
- 自旋阈值可调(-XX:PreBlockSpin,默认10次),超限或自旋中锁被释放,就膨胀为重量锁
- 适合临界区极短、竞争不激烈的场景;自旋耗CPU,长时间自旋反而得不偿失
重量锁:终极保障的“操作系统级仲裁”
当自旋失败或竞争激烈,JVM将锁膨胀为重量级:对象头指向一个ObjectMonitor(C++实现),线程阻塞进入Monitor的EntryList,由操作系统调度挂起与唤醒。
- 此时synchronized实际等效于调用操作系统mutex,开销最大
- 锁一旦升级为重量级,不会降级(JVM设计如此,为简化实现)
- 可通过jstack观察线程状态为BLOCKED,说明已在争抢重量锁
记住核心逻辑比记数字更重要:偏向锁省判断、轻量锁省切换、重量锁保正确。这些是JVM自动管理的优化策略,开发者通常只需写好synchronized或Lock,不必手动干预锁形态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











