轻量级锁自旋失败后立即升级为重量级锁,即替换mark word为monitor指针、将线程加入_entrylist并park挂起,而非无限自旋或降级;jvm控制有限自旋次数(如10–100次),失败即膨胀;该机制不支持多线程同时自旋抢锁。

Java 中轻量级锁在自旋过程中竞争失败时,会升级为重量级锁(即挂起线程、进入 Monitor 等待队列),而不是继续无限自旋或降级。
自旋失败后触发锁膨胀
当一个线程尝试获取轻量级锁(即通过 CAS 将对象头 Mark Word 更新为指向当前线程栈帧中 Lock Record 的指针)失败,说明已有其他线程持有该锁。此时 JVM 不会反复重试 CAS,而是进入锁膨胀流程:
- 将对象头 Mark Word 中的轻量级锁标记(biased bit = 0,lock bit = 01)替换为指向 Monitor 对象的指针(lock bit = 10)
- 将当前线程封装为 ObjectWaiter,加入 Monitor 的 _EntryList 队列
- 调用系统级 park() 挂起线程,等待被唤醒
- 原持有锁的线程在释放锁时,会唤醒 _EntryList 中的一个线程(通常 FIFO)
自旋次数有限且由 JVM 控制
HotSpot 并不开放自旋次数配置给用户,但内部有默认策略:
- 在锁竞争较轻时,可能允许少量自旋(如 10–100 次 CAS 尝试),具体取决于锁的“热度”和历史表现
- 若自旋未成功,立即执行锁膨胀,避免 CPU 空转浪费
- 自旋逻辑位于 ObjectSynchronizer::fast_enter 和 ObjectSynchronizer::slow_enter 中,失败后跳转至后者完成膨胀
注意:轻量级锁不支持多线程同时自旋抢锁
轻量级锁的设计初衷是“无竞争场景下的快速路径”,它本身不具备公平性或多线程协作机制:
- 只有一个线程能成功 CAS 设置 Lock Record;其余线程一旦失败,就不再尝试自旋,直接走膨胀流程
- 不存在类似 ReentrantLock 中 tryAcquire 的多次重试逻辑,也不使用自旋锁(SpinLock)结构
- 因此“自旋过程中的竞争失败处理”本质就是“放弃自旋,升级锁”
实际影响与优化提示
频繁触发轻量级锁膨胀,往往意味着存在真实竞争,应从应用层优化:
- 减少共享对象的临界区长度(例如拆分大同步块)
- 考虑使用无锁数据结构(如 ConcurrentLinkedQueue)或分段锁(如 ConcurrentHashMap)
- 避免在循环内反复 synchronized 同一对象(易导致连续膨胀)
- 可通过 -XX:+PrintGCDetails 或 JFR 事件观察 MonitorInflation 次数,定位热点锁
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











