synchronized锁升级过程(无锁→偏向锁→轻量级锁→重量级锁)

该文章已生成可运行项目,

synchronized 是 Java 中最基础的同步机制,其底层通过锁升级​(Lock Escalation)机制优化性能,根据竞争激烈程度动态调整锁的粒度,从无锁逐步升级为偏向锁、轻量级锁,最终可能升级为重量级锁。以下是完整的锁升级过程及原理详解。

一、锁升级的核心背景

synchronized 的锁升级是为了解决不同竞争场景下的性能问题:

  • 无竞争或低竞争​:通过轻量级机制(如偏向、CAS)减少同步开销。
  • 高竞争​:升级为重量级锁(内核级互斥),避免用户态频繁竞争导致的性能损耗。

二、锁升级的四个阶段

锁升级是单向不可逆的过程(无锁 → 偏向锁 → 轻量级锁 → 重量级锁),每个阶段的触发条件和底层实现不同。

1. 无锁状态(No Lock)​
  • 状态特征​:对象头中无锁标记(Mark Word 的锁标志位为 01,表示无锁)。
  • 行为​:任何线程均可自由访问同步块,无需同步操作。
  • 触发升级条件​:当第一个线程进入同步块时,尝试获取偏向锁,触发无锁→偏向锁的升级。
2. 偏向锁(Biased Lock)​
  • 核心目标​:优化同一线程多次进入同一同步块的场景(如循环中重复加锁)。
  • 状态特征​:
    • Mark Word 的锁标志位为 01,偏向标志位(Biased)为 1
    • 记录当前持有锁的线程 ID(Thread ID)。
  • 加锁逻辑​:
    当线程首次进入同步块时,JVM 检查对象头的 Mark Word
    • 若未偏向(无锁或已撤销偏向),则通过 CAS 操作将 Mark Word 的线程 ID 设置为当前线程,标记为偏向锁(无需原子操作,因首次设置无竞争)。
    • 若已偏向当前线程(线程 ID 匹配),则直接进入同步块(无需任何同步操作)。
  • 解锁逻辑​:
    偏向锁的解锁无需原子操作(因仅当前线程持有),直接清除 Mark Word 中的线程 ID 和偏向标志。
  • 触发升级条件​:
    其他线程尝试进入同步块时(即检测到 Mark Word 中的线程 ID 与当前线程不一致),偏向锁被撤销(Unbias),升级为轻量级锁。
3. 轻量级锁(Lightweight Lock)​
  • 核心目标​:优化低竞争场景​(多个线程交替获取锁,但竞争不激烈)。
  • 状态特征​:
    • Mark Word 的锁标志位为 00(表示轻量级锁)。
    • Mark Word 存储的是指向线程栈中锁记录(Lock Record)​的指针。
  • 加锁逻辑​(CAS 操作):
    1. 线程在栈中创建一个 Lock Record(记录锁对象的引用和状态)。
    2. 通过 CAS 操作将对象头的 Mark Word 替换为 Lock Record 的指针(原子操作)。
    3. 若 CAS 成功,线程获得锁;若失败,说明存在竞争,进入锁膨胀流程。
  • 解锁逻辑​:
    线程通过 CAS 操作将对象头的 Mark Word 恢复为原始值(即释放锁)。若 CAS 失败(其他线程已修改),则触发锁膨胀。
  • 触发升级条件​:
    多个线程频繁竞争锁​(CAS 失败次数超过阈值,如 10 次),轻量级锁膨胀为重量级锁。
4. 重量级锁(Heavyweight Lock)​
  • 核心目标​:处理高竞争场景​(多个线程激烈争夺锁,CAS 无法高效解决)。
  • 状态特征​:
    • Mark Word 的锁标志位为 10(表示重量级锁)。
    • Mark Word 存储指向 Monitor 对象的指针(Monitor 是 JVM 内核级的锁结构)。
  • 加锁逻辑​:
    线程通过 monitorenter 指令尝试获取 Monitor 锁:
    1. MonitorOwner 为空,线程成为 Owner,获得锁。
    2. Owner 非空,线程进入 EntryList(阻塞队列)等待。
  • 解锁逻辑​:
    线程通过 monitorexit 指令释放 Monitor 锁:
    1. Owner 是当前线程,释放锁并将 Owner 置空(或唤醒 EntryList 中的线程)。
    2. Owner 非当前线程(非法释放),抛出 IllegalMonitorStateException
  • 触发降级条件​:
    重量级锁无法降级为轻量级或偏向锁(一旦升级为重量级,后续只能通过解锁恢复无锁状态)。

三、锁升级的底层实现细节

1. 对象头(Mark Word)的结构

Mark Word 是对象头的核心部分,存储锁状态、线程 ID、哈希码等信息。其结构随锁状态变化而变化(以 64 位 JVM 为例):

锁状态锁标志位偏向标志位存储内容
无锁010哈希码(HashCode)、GC 分代年龄(Generational GC Age)
偏向锁011偏向线程 ID、偏向标志位、GC 分代年龄
轻量级锁00-指向栈中 Lock Record 的指针
重量级锁10-指向 Monitor 对象的指针
2. 偏向锁的撤销(Unbias)​

当其他线程尝试进入同步块时,JVM 会检测到偏向锁的线程 ID 与当前线程不一致,触发偏向锁撤销:

  1. 检查偏向锁的线程是否存活(通过 Thread 对象的 alive 状态)。
  2. 若线程存活,通过 CAS 操作清除 Mark Word 中的线程 ID 和偏向标志,升级为轻量级锁。
  3. 若线程不存活(已终止),直接清除偏向标记,升级为轻量级锁。
3. 轻量级锁的膨胀(Inflation)​

当轻量级锁的 CAS 操作失败次数超过阈值(默认 10 次),JVM 会认为存在激烈竞争,触发膨胀:

  1. 创建 Monitor 对象(存储在 JVM 的 Monitor 列表中)。
  2. 通过 monitorenter 指令将 Mark Word 替换为 Monitor 指针,锁标志位改为 10(重量级锁)。

四、锁升级的性能影响

锁类型适用场景性能特点
无锁无线程竞争无同步开销,性能最优。
偏向锁单线程重复加锁(如循环)无需原子操作,性能接近无锁(仅需设置线程 ID)。
轻量级锁低竞争(多线程交替加锁)通过 CAS 减少内核态切换,性能优于重量级锁。
重量级锁高竞争(多线程激烈争夺)涉及内核态互斥(pthread_mutex),性能最差(但能保证正确性)。

五、总结

synchronized 的锁升级机制通过动态调整锁的粒度,在不同竞争场景下选择最优的同步策略:

  • 无锁 → 偏向锁:优化单线程重复加锁。
  • 偏向锁 → 轻量级锁:处理多线程低竞争场景(CAS 替代内核操作)。
  • 轻量级锁 → 重量级锁:应对高竞争场景(内核级互斥保证正确性)。

理解锁升级过程有助于优化同步代码(如避免不必要的偏向锁撤销、减少高竞争场景的锁使用),从而提升应用性能。

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值