轻量级锁升级为重量级锁并非基于自旋次数或时间阈值,而是检测到真实线程竞争即刻触发;只要另一线程在持有者未释放锁时尝试获取,jvm就立即调用inflate()膨胀,不提供自旋等待机会。
轻量级锁升级为重量级锁,不是看自旋次数或时间阈值,而是一旦检测到真实线程竞争就立即触发。jvm不给轻量级锁“自旋等待”的机会,它只适用于无重叠的交替执行场景;只要另一个线程在持有者还没释放锁时就来抢,膨胀即刻发生。
触发膨胀的两个明确临界条件
以下任一情况出现,JVM就会调用 ObjectSynchronizer::inflate() 启动锁膨胀:
- 多线程同时尝试获取同一把轻量级锁:线程A已通过CAS将对象Mark Word指向自己的栈中Lock Record;线程B发现Mark Word既不是无锁状态(01),也不指向自己(且非空),直接判定竞争存在
- 持有锁的线程尚未退出同步块,另一线程已发起加锁请求:这是最常见情形。注意——JVM不会让线程B自旋等待,也不会重试CAS,而是立刻走膨胀流程
底层源码关键逻辑(HotSpot synchronizer.cpp)
膨胀入口函数 inflate(Thread* self, oop object) 的核心判断逻辑如下:
- 读取对象当前Mark Word:
markOop mark = object->mark(); - 若已是重量级锁(
mark->has_monitor()返回true),直接返回关联的Monitor指针 - 若Mark Word处于
INFLATING中间态(其他线程正在膨胀),当前线程循环等待 - 若Mark Word显示为轻量级锁(
mark->has_locker()为true),则进入正式膨胀步骤
膨胀三步走:从Lock Record到ObjectMonitor
确认需膨胀后,JVM执行原子性操作完成状态切换:
-
分配Monitor对象:调用
omAlloc(self)在C++堆中创建ObjectMonitor*实例,初始化_owner、_EntryList、_WaitSet等字段 -
更新对象头:使用CAS将对象Mark Word设置为指向新Monitor的指针,并将锁标记位设为
10(重量级锁标识) -
移交所有权:将原轻量级锁持有者线程设为Monitor的
_owner;若该线程已退出同步块,则_owner置为null,后续线程可直接获取
为什么不是“自旋失败后才升级”?
这是常见误解。Java轻量级锁本身不是自旋锁,它的设计目标是零竞争下的低开销加锁,而非应对竞争。一旦竞争发生,继续自旋没有意义——因为:
- 轻量级锁的Lock Record在线程栈上,无法被其他线程感知或协作更新
- 自旋需消耗CPU,而竞争意味着至少一个线程必然要阻塞,不如直接交由操作系统调度
- 重量级锁的Monitor结构天然支持队列管理与唤醒机制,更适合多线程协同
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











