Java锁升级完整全过程详解(无锁→偏向锁→轻量级锁→重量级锁)

一、核心前置知识

1.1 锁升级核心特性

  • 单向不可逆:锁状态只能从低级别向高级别升级(无锁→偏向锁→轻量级锁→重量级锁),绝不降级,是JVM为简化同步逻辑、提升运行效率的设计。

  • 状态切换核心:所有锁状态的变更,本质都是修改对象头的Mark Word(标记字段),Mark Word存储锁状态、线程ID、哈希码、分代年龄等核心数据。

  • 设计核心目的:根据线程竞争激烈程度,自适应切换锁机制,在无竞争/低竞争场景最大化降低同步开销,高竞争场景保证线程安全。

1.2 三种核心锁的适配场景

  • 偏向锁:单线程反复加锁,完全无竞争

  • 轻量级锁:多线程交替执行,竞争轻微、临界区代码执行快

  • 重量级锁:多线程同时争抢,竞争激烈、临界区执行时间长

二、锁升级完整阶段流程

阶段一:无锁状态(初始状态)

1. 触发条件

对象刚创建完成,暂无任何线程尝试获取该对象锁。

2. Mark Word 特征

存储对象哈希码、分代年龄、偏向锁标识(0),无任何线程指针、锁标记。

3. 补充说明

JVM默认开启偏向锁延迟机制(默认4秒),JVM启动初期创建的对象,初始为无锁状态,延迟结束后新建对象才会默认开启偏向锁。目的是规避JVM启动阶段大量线程竞争,频繁触发偏向锁撤销带来的性能损耗。

阶段二:无锁 → 偏向锁(无竞争优化)

1. 触发条件

第一个线程首次获取锁,全程无其他线程竞争。

2. 核心执行逻辑
  1. JVM直接修改对象Mark Word,将偏向锁标识置为1,把当前线程ID写入Mark Word;

  2. 该过程无需CAS操作,仅简单赋值,开销极低;

  3. 后续该线程重复加锁/解锁时,只需校验Mark Word中的线程ID是否为自身:匹配则直接进入临界区,无任何同步开销。

3. 核心作用

解决「单线程频繁加锁解锁」的无效性能消耗,是最轻量级的锁优化机制。

阶段三:偏向锁 → 轻量级锁(出现轻微竞争)

1. 触发条件

出现第二个不同线程尝试获取锁,产生线程竞争,触发偏向锁撤销与升级。

2. 偏向锁撤销逻辑
  1. JVM短暂暂停持有偏向锁的线程,判断其是否仍在执行临界区代码;

  2. 若持有线程已执行完毕:撤销偏向锁,对象恢复为无锁状态,新线程可重新竞争锁;

  3. 若持有线程仍在执行:直接升级为轻量级锁。

3. 轻量级锁加锁核心逻辑(CAS机制)
  1. 所有竞争线程在自身虚拟机栈中创建「Displaced Mark Word(位移Mark Word)」,拷贝对象原始的Mark Word数据;

  2. 线程通过CAS自旋尝试修改对象Mark Word:将原始Mark Word替换为「指向当前线程栈中Displaced Mark Word的指针」;

  3. CAS成功:线程获取轻量级锁,执行临界区代码;

  4. CAS失败:线程不挂起,持续自旋重试抢锁。

4. 轻量级锁解锁核心逻辑
  1. 线程执行完临界区后,通过CAS尝试将栈中Displaced Mark Word(原始数据)写回对象Mark Word;

  2. CAS成功:解锁完成,对象恢复无锁状态;

  3. CAS失败:说明已有其他线程竞争锁,触发轻量级锁升级。

5. 核心作用

通过CAS自旋替代操作系统线程挂起/唤醒,规避重量级锁的高开销,适配短时间、低竞争的同步场景。

阶段四:轻量级锁 → 重量级锁(竞争激烈)

1. 触发条件(满足其一即可升级)
  • 线程自旋抢锁达到最大自旋次数(默认10次,支持自适应自旋)仍未获取锁;

  • 竞争线程数量增多,自旋开销超过线程阻塞开销;

  • 轻量级锁解锁时CAS失败,检测到并发竞争。

2. 重量级锁升级核心逻辑
  1. 锁膨胀:对象Mark Word不再指向线程栈的Displaced Mark Word,改为指向操作系统Monitor(监视器锁);

  2. 状态切换:失败的线程终止自旋,由JVM交由操作系统调度,线程被挂起,进入阻塞队列;

  3. 锁等待:阻塞队列中的线程被动等待,不会主动抢锁;

  4. 锁唤醒:持有锁的线程执行完毕释放锁后,操作系统唤醒阻塞队列中的线程,重新竞争锁。

3. 核心特点

依赖操作系统内核实现同步,线程会阻塞、唤醒,开销大,但能稳定支撑高并发、长时间锁竞争场景,保证线程绝对安全。

三、锁升级整体流转总图

无锁(初始)→【单线程无竞争】→ 偏向锁 →【出现多线程竞争】→ 撤销偏向锁/轻量级锁 →【自旋超时/竞争加剧】→ 重量级锁(永久不降级)

四、关键补充知识点

1. 自适应自旋

JVM不会固定自旋次数,会根据历史抢锁结果自适应调整:上次自旋抢锁成功,本次增加自旋次数;上次自旋失败,本次减少甚至取消自旋,优化性能。

2. 锁升级不可逆的原因

锁升级后会创建Monitor对象、维护线程阻塞队列,若频繁降级再升级,会产生大量对象创建、销毁、队列维护的冗余开销,JVM为权衡性能,设计为单向升级。

3. 锁消除(拓展优化)

不属于锁升级流程,是JVM编译期优化:对于局部变量锁、无共享竞争的无效锁,直接彻底消除,避免无意义的同步开销。

五、各锁状态核心对比总结

锁类型适用场景核心机制开销是否阻塞线程
偏向锁单线程重复加锁、无竞争标记线程ID,无CAS极低否
轻量级锁多线程交替执行、轻微竞争CAS自旋+Displaced Mark Word低否(仅自旋)
重量级锁多线程并发争抢、激烈竞争操作系统Monitor阻塞队列高是(线程阻塞)

(注:部分内容可能由 AI 生成)

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐