技术演进中的开发沉思-349:高效并发(下)

今天梳理的这五点是 HotSpot 虚拟机对synchronized最核心、最实用的分层锁优化手段 (JDK1.6 + 引入),核心设计思路是 **「按需优化、分层适配」------ 根据多线程对锁的 竞争程度 **(无竞争→轻度竞争→重度竞争),从低开销到高开销动态切换锁的实现方式,同时通过锁消除、锁粗化消除无意义的锁操作,最终让原本的 "重量级锁" 在大部分场景下都能做到低开销执行。

这些优化并非孤立存在,偏向锁、轻量级锁、自旋锁 是一套完整的锁升级链路 (不可逆),而锁消除、锁粗化 是独立的通用优化手段(可与任意锁状态配合)。下面结合你的核心要点,补充底层实现细节、执行逻辑、适配场景,让每个优化的作用和原理更清晰,同时明确各优化间的关联。

一、偏向锁

核心原理

针对单个线程重复获取同一把锁 的无竞争场景,彻底消除锁的获取 / 释放开销 ------ 锁会「偏向」于第一个获取它的线程,后续该线程重入锁时,无需任何 CAS、加解锁操作,仅需判断对象头的线程 ID 标记即可。

执行关键细节

  1. 锁的标记存储在Java 对象头的 Mark Word 中,无竞争时 Mark Word 会设置为偏向锁状态 ,并记录第一个获取锁的线程 ID
  2. 该线程后续再次进入同步代码块,仅需做一次简单的线程 ID 匹配判断:匹配成功则直接获锁,无任何额外开销;
  3. 若有其他线程尝试获取该锁 (出现竞争),HotSpot 会撤销偏向锁 ,并将其升级为轻量级锁(升级过程不可逆)。

核心价值

将无竞争场景下的锁开销降到近乎为 0,这是开发中最常见的场景(如单线程执行同步代码、方法内的局部同步对象),也是锁优化的基础。

二、轻量级锁

核心原理

针对多个线程交替获取同一把锁 的轻度竞争场景(无同时竞争),放弃依赖操作系统的重量级互斥锁,改用CAS(比较并交换) 实现锁的获取与释放,避免内核态 / 用户态的切换开销(该开销远大于 CAS 操作)。

执行关键细节

  1. 偏向锁撤销后,HotSpot 将对象头 Mark Word 改为轻量级锁状态
  2. 线程获取锁时,会在自己的栈帧中创建锁记录(Lock Record) ,并通过CAS 操作 将对象头的 Mark Word 替换为指向该锁记录的指针:
    • CAS 成功:表示获锁成功,执行同步代码;
    • CAS 失败:表示存在锁竞争,触发自旋锁等待,而非直接阻塞;
  3. 线程释放锁时,再次通过CAS 操作将对象头的 Mark Word 恢复为原始状态,完成解锁。

核心价值

用户态的 CAS 操作 替代内核态的重量级锁,解决轻度竞争下的锁开销问题,是偏向锁到重量级锁的「过渡锁」。

三、自旋锁 / 自适应自旋

核心定位

自旋锁并非独立的锁状态,而是轻量级锁处理竞争的配套策略 ------ 解决 "CAS 获锁失败后,线程该如何等待" 的问题,核心是用 CPU 循环等待替代线程内核态阻塞

1. 基础自旋锁

核心原理

线程 CAS 获取轻量级锁失败时,不立即进入操作系统的内核阻塞队列 ,而是在用户态循环执行 CAS 操作(自旋),等待持有锁的线程快速释放锁 ------ 因为大部分场景下,锁的持有时间极短,自旋的 CPU 开销远小于线程阻塞 / 唤醒的内核开销。

关键细节
  • 自旋次数默认有限制 (JDK1.6 默认 10 次,可通过 JVM 参数-XX:PreBlockSpin调整);
  • 自旋成功:获锁执行同步代码;自旋失败:轻量级锁升级为重量级锁,当前线程进入内核态阻塞。

2. 自适应自旋(JDK1.6 + 默认开启,更智能)

核心原理

对基础自旋锁的优化,自旋次数不再固定 ,由 HotSpot根据历史竞争情况动态调整,让自旋的次数更贴合实际场景。

关键细节
  • 当前线程自旋成功过 (等待的锁持有者快速释放),则下次自旋时会增加自旋次数(认为此次锁持有时间也短);
  • 当前线程自旋失败过 (等待的锁持有者耗时较长),则下次自旋时会减少甚至取消自旋(避免 CPU 空转);
  • 自适应自旋由 JVM 自动调控,无需开发者手动配置,是生产环境的默认策略。

核心价值

避免轻度竞争下的线程内核阻塞,最大化利用 CPU 资源,同时通过有限自旋 / 自适应自旋防止 CPU 空转。

四、锁消除

核心原理

HotSpot 通过逃逸分析 ,判断同步代码块中的锁对象是否仅在当前线程内使用 (即「无逃逸」),若确定无线程竞争,会在即时编译(JIT)阶段直接消除该锁的所有加解锁操作,彻底避免无意义的锁开销。

执行关键细节

  1. 逃逸分析:JVM 的核心分析技术,判断对象的作用域 ------ 若对象仅在当前方法 / 线程内创建、使用,未暴露到方法外 / 其他线程,则为「无逃逸」;
  2. 锁消除仅针对无逃逸的局部锁对象 ,JIT 编译时会直接删除synchronized对应的monitorenter/monitorexit指令;
  3. 无需开发者做任何代码修改,由 JVM 自动完成。

典型示例

java 复制代码
// StringBuffer的append方法被synchronized修饰,但sb是局部对象,无逃逸
public String test() {
    StringBuffer sb = new StringBuffer();
    sb.append("a").append("b"); // 无竞争,JVM会消除append中的锁
    return sb.toString();
}

核心价值

去掉完全不必要的锁操作,让同步代码直接以无锁方式执行,无任何锁开销。

五、锁粗化

核心原理

若线程连续多次对同一把锁进行获取 / 释放操作 (如循环内的同步代码块),HotSpot 会将多个连续的锁操作合并为一次 ,即扩大同步代码块的范围,减少锁的获取 / 释放次数,从而降低锁操作的总开销。

执行关键细节

  1. 触发场景:循环内的同步代码块、连续多次调用同一锁对象的同步方法;
  2. JVM 会自动将多次monitorenter/monitorexit指令合并为一次加锁、一次解锁,同步范围覆盖所有连续的操作;
  3. 锁粗化不会无限扩大同步范围------JVM 会保证合并后的同步代码块不会引发更激烈的锁竞争,否则会停止粗化。

典型示例

java 复制代码
// 优化前:循环1000次,1000次加解锁
for (int i = 0; i < 1000; i++) {
    synchronized (lock) {
        count++;
    }
}

// 锁粗化后:JVM自动合并,仅1次加解锁
synchronized (lock) {
    for (int i = 0; i < 1000; i++) {
        count++;
    }
}

核心价值

减少锁的获取 / 释放这一高频操作的次数,降低累计的锁开销,同时不影响线程安全。


六、HotSpot 锁优化核心关联与整体流程

这五点优化并非孤立,而是分层配合、按需触发,形成一套完整的 synchronized 优化体系,整体执行流程和关联关系如下:

复制代码
无竞争场景 → 偏向锁(标记线程ID,零开销)
   ↓ 出现其他线程竞争
轻度竞争(交替获锁) → 轻量级锁(CAS实现)+ 自旋锁/自适应自旋(循环等待)
   ↓ 自旋失败/同时竞争(重度竞争)
重度竞争场景 → 重量级锁(内核互斥锁,保证安全)

【通用优化】:无论上述哪种锁状态,JVM都会同时执行
1. 锁消除:消除无逃逸的冗余锁(无锁开销)
2. 锁粗化:合并连续锁操作(减少锁操作次数)

核心原则不可逆的锁升级 + 按需的轻量级实现 + 无意义锁的消除,让 synchronized 在 90% 的实际开发场景下,都能做到「低开销、高安全」。

最后小结

  1. 偏向锁:无竞争零开销,仅标记获锁线程 ID,有竞争则升级为轻量级锁;
  2. 轻量级锁:轻度竞争用 CAS 替代重量级锁,避免内核切换开销;
  3. 自旋 / 自适应自旋:轻量级锁的配套等待策略,用 CPU 循环替代线程阻塞,自适应自旋更智能;
  4. 锁消除:基于逃逸分析,自动删除无竞争的冗余锁,无需开发者干预;
  5. 锁粗化:合并同一锁的连续加解锁操作,减少锁操作次数,降低累计开销。
相关推荐
2401_838472512 小时前
Python多线程与多进程:如何选择?(GIL全局解释器锁详解)
jvm·数据库·python
2301_822363602 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
shejizuopin2 小时前
基于SSM的高校旧书交易系统的设计与实现(任务书)
java·mysql·毕业设计·论文·任务书·基于ssm的·高校旧书交易系统的设计与实现
m0_561359672 小时前
Python面向对象编程(OOP)终极指南
jvm·数据库·python
1candobetter2 小时前
JAVA后端开发——Spring Boot 组件化自动配置机制
java·开发语言·spring boot
yufuu982 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
码农小卡拉2 小时前
MyBatis-Flex 全面解析与实战教程:轻量高效的 MyBatis 增强方案
java·mybatis
没有bug.的程序员2 小时前
Spring Boot 与 Sleuth:分布式链路追踪的集成、原理与线上故障排查实战
java·spring boot·分布式·后端·分布式链路追踪·sleuth·线上故障排查
专注VB编程开发20年2 小时前
无 $ 后缀的变体版函数(Mid、InStr)
java·开发语言