Java锁升级完整全过程详解(无锁→偏向锁→轻量级锁→重量级锁)
Java锁升级完整全过程详解(无锁→偏向锁→轻量级锁→重量级锁)
一、核心前置知识
1.1 锁升级核心特性
-
单向不可逆:锁状态只能从低级别向高级别升级(无锁→偏向锁→轻量级锁→重量级锁),绝不降级,是JVM为简化同步逻辑、提升运行效率的设计。
-
状态切换核心:所有锁状态的变更,本质都是修改对象头的Mark Word(标记字段),Mark Word存储锁状态、线程ID、哈希码、分代年龄等核心数据。
-
设计核心目的:根据线程竞争激烈程度,自适应切换锁机制,在无竞争/低竞争场景最大化降低同步开销,高竞争场景保证线程安全。
1.2 三种核心锁的适配场景
-
偏向锁:单线程反复加锁,完全无竞争
-
轻量级锁:多线程交替执行,竞争轻微、临界区代码执行快
-
重量级锁:多线程同时争抢,竞争激烈、临界区执行时间长
二、锁升级完整阶段流程
阶段一:无锁状态(初始状态)
1. 触发条件
对象刚创建完成,暂无任何线程尝试获取该对象锁。
2. Mark Word 特征
存储对象哈希码、分代年龄、偏向锁标识(0),无任何线程指针、锁标记。
3. 补充说明
JVM默认开启偏向锁延迟机制(默认4秒),JVM启动初期创建的对象,初始为无锁状态,延迟结束后新建对象才会默认开启偏向锁。目的是规避JVM启动阶段大量线程竞争,频繁触发偏向锁撤销带来的性能损耗。
阶段二:无锁 → 偏向锁(无竞争优化)
1. 触发条件
第一个线程首次获取锁,全程无其他线程竞争。
2. 核心执行逻辑
-
JVM直接修改对象Mark Word,将偏向锁标识置为1,把当前线程ID写入Mark Word;
-
该过程无需CAS操作,仅简单赋值,开销极低;
-
后续该线程重复加锁/解锁时,只需校验Mark Word中的线程ID是否为自身:匹配则直接进入临界区,无任何同步开销。
3. 核心作用
解决「单线程频繁加锁解锁」的无效性能消耗,是最轻量级的锁优化机制。
阶段三:偏向锁 → 轻量级锁(出现轻微竞争)
1. 触发条件
出现第二个不同线程尝试获取锁,产生线程竞争,触发偏向锁撤销与升级。
2. 偏向锁撤销逻辑
-
JVM短暂暂停持有偏向锁的线程,判断其是否仍在执行临界区代码;
-
若持有线程已执行完毕:撤销偏向锁,对象恢复为无锁状态,新线程可重新竞争锁;
-
若持有线程仍在执行:直接升级为轻量级锁。
3. 轻量级锁加锁核心逻辑(CAS机制)
-
所有竞争线程在自身虚拟机栈中创建「Displaced Mark Word(位移Mark Word)」,拷贝对象原始的Mark Word数据;
-
线程通过CAS自旋尝试修改对象Mark Word:将原始Mark Word替换为「指向当前线程栈中Displaced Mark Word的指针」;
-
CAS成功:线程获取轻量级锁,执行临界区代码;
-
CAS失败:线程不挂起,持续自旋重试抢锁。
4. 轻量级锁解锁核心逻辑
-
线程执行完临界区后,通过CAS尝试将栈中Displaced Mark Word(原始数据)写回对象Mark Word;
-
CAS成功:解锁完成,对象恢复无锁状态;
-
CAS失败:说明已有其他线程竞争锁,触发轻量级锁升级。
5. 核心作用
通过CAS自旋替代操作系统线程挂起/唤醒,规避重量级锁的高开销,适配短时间、低竞争的同步场景。
阶段四:轻量级锁 → 重量级锁(竞争激烈)
1. 触发条件(满足其一即可升级)
-
线程自旋抢锁达到最大自旋次数(默认10次,支持自适应自旋)仍未获取锁;
-
竞争线程数量增多,自旋开销超过线程阻塞开销;
-
轻量级锁解锁时CAS失败,检测到并发竞争。
2. 重量级锁升级核心逻辑
-
锁膨胀:对象Mark Word不再指向线程栈的Displaced Mark Word,改为指向操作系统Monitor(监视器锁);
-
状态切换:失败的线程终止自旋,由JVM交由操作系统调度,线程被挂起,进入阻塞队列;
-
锁等待:阻塞队列中的线程被动等待,不会主动抢锁;
-
锁唤醒:持有锁的线程执行完毕释放锁后,操作系统唤醒阻塞队列中的线程,重新竞争锁。
3. 核心特点
依赖操作系统内核实现同步,线程会阻塞、唤醒,开销大,但能稳定支撑高并发、长时间锁竞争场景,保证线程绝对安全。
三、锁升级整体流转总图
无锁(初始)→【单线程无竞争】→ 偏向锁 →【出现多线程竞争】→ 撤销偏向锁/轻量级锁 →【自旋超时/竞争加剧】→ 重量级锁(永久不降级)
四、关键补充知识点
1. 自适应自旋
JVM不会固定自旋次数,会根据历史抢锁结果自适应调整:上次自旋抢锁成功,本次增加自旋次数;上次自旋失败,本次减少甚至取消自旋,优化性能。
2. 锁升级不可逆的原因
锁升级后会创建Monitor对象、维护线程阻塞队列,若频繁降级再升级,会产生大量对象创建、销毁、队列维护的冗余开销,JVM为权衡性能,设计为单向升级。
3. 锁消除(拓展优化)
不属于锁升级流程,是JVM编译期优化:对于局部变量锁、无共享竞争的无效锁,直接彻底消除,避免无意义的同步开销。
五、各锁状态核心对比总结
| 锁类型 | 适用场景 | 核心机制 | 开销 | 是否阻塞线程 |
|---|---|---|---|---|
| 偏向锁 | 单线程重复加锁、无竞争 | 标记线程ID,无CAS | 极低 | 否 |
| 轻量级锁 | 多线程交替执行、轻微竞争 | CAS自旋+Displaced Mark Word | 低 | 否(仅自旋) |
| 重量级锁 | 多线程并发争抢、激烈竞争 | 操作系统Monitor阻塞队列 | 高 | 是(线程阻塞) |
(注:部分内容可能由 AI 生成)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)