(图片出自黑马程序员)
一、锁膨胀的触发条件
- 多线程竞争:当多个线程尝试获取同一个对象的锁。
- 自旋失败:竞争线程在轻量级锁阶段通过自旋(循环重试)尝试获取锁失败,达到阈值(如自旋次数超过JVM动态调整的阈值)。
二、轻量级锁 → Monitor锁的详细过程
1. 初始状态(轻量级锁)
- 场景:单线程或低并发环境下,线程通过CAS操作将对象头的
Mark Word
替换为指向线程栈中锁记录的指针。 - 对象头标记:
Mark Word
标记为轻量级锁状态(标志位00
)。 - 特点:无需操作系统介入,性能开销低。
java对象头:

Mark Word结构:

2. 竞争发生
-
线程B尝试获取锁:当另一个线程(线程1)尝试通过CAS获取锁时,发现对象头中的指针已被线程0占用。
-
自旋尝试:线程1进入自旋状态(循环重试),等待线程0释放锁。
3. 自旋失败,触发锁膨胀
- 自旋超限:若线程1自旋一定次数后仍未获取锁(默认策略或适应性自旋策略判断失败),JVM判定为高竞争场景。
- 锁膨胀流程:
-
分配Monitor对象:JVM为当前对象创建一个
ObjectMonitor
(Monitor锁的核心结构)。 -
修改对象头:将对象头的
Mark Word
替换为指向ObjectMonitor
的指针,标志位变为重量级锁(10
)。
-
线程状态变更:
- 线程0继续执行同步代码,但锁的管理移交至
ObjectMonitor
。 - 线程1被挂起(进入阻塞状态),加入
ObjectMonitor
的等待队列(EntryList)。
- 线程0继续执行同步代码,但锁的管理移交至
-
4. Monitor锁的运行机制
- ObjectMonitor结构:
owner
:指向持有锁的线程。EntryList
:阻塞等待锁的线程队列。WaitSet
:调用wait()
方法的线程队列。
- 锁竞争失败后的行为:
- 线程竞争锁失败后直接进入阻塞状态(通过操作系统互斥量实现),不再自旋。
- 锁释放后,JVM从
EntryList
中唤醒线程重新竞争。
三、总结
- 锁膨胀不可逆:一旦升级为重量级锁,不会降级(除非对象被回收后重新创建)。
- 性能权衡:
- 轻量级锁:低开销(CAS + 自旋),适合低竞争场景。
- Monitor锁:高开销(涉及操作系统内核态切换),但避免CPU空转,适合高竞争场景。
- 适应性自旋优化:JDK 6后JVM根据历史竞争情况动态调整自旋次数。