愆蟠唉
2025-6-13 16:13:15
Java 锁升级机制详解
引言
最近有个三年左右的兄弟面试java 被问到这样一道经典的八股文面试题: 你讲讲java里面的锁升级? 他感觉回答的不是很好,然后回去找资料学习了一波,然后下面是他输出的文章,希望对找工作的其他朋友也有些帮助。
1. 概述
Java 的锁升级机制是 JVM 在 JDK 1.6 后引入的重要优化策略,目的是在多线程环境下平衡 线程安全 与 性能开销 。通过动态调整锁的复杂度,JVM 根据竞争强度逐步升级锁的状态,避免在低竞争场景下使用高成本的重量级锁。
2. 锁类型及特点
锁类型 适用场景 性能开销 核心机制 无锁(Unlocked) 无线程竞争极低直接通过 CAS 操作尝试获取锁。偏向锁(Biased Locking) 单线程重复访问(无竞争)极低对象头记录偏向线程 ID,后续同一线程无需竞争,直接获取锁。轻量级锁(Lightweight Lock) 低竞争(多个线程交替访问)中等通过 CAS 自旋尝试获取锁,避免操作系统级别的阻塞。重量级锁(Heavyweight Lock) 高竞争(长时间阻塞或高并发)高依赖操作系统互斥量(Mutex),线程被挂起并排队等待。3. 锁升级的过程
锁升级路径为:无锁 → 偏向锁 → 轻量级锁 → 重量级锁 ,且 不可逆 (只能升级,不能降级)。
3.1 无锁 → 偏向锁
触发条件 :第一个线程访问同步代码块时。
过程 :
JVM 通过 CAS 操作将对象头的 Mark Word 标记为偏向锁。
记录当前线程 ID 和偏向时间戳。
后续同一线程再次访问时,直接通过比对线程 ID 获取锁(无需 CAS 操作)。
3.2 偏向锁 → 轻量级锁
触发条件 :第二个线程尝试获取同一锁(出现竞争)。
过程 :
偏向锁失效,JVM 撤销偏向锁(可能触发 STW,Stop-The-World)。
线程通过自旋(Spin)和 CAS 操作尝试获取锁。
若自旋成功,则升级为轻量级锁;否则继续自旋或升级为重量级锁。
3.3 轻量级锁 → 重量级锁
触发条件 :
自旋次数超过阈值(默认 10 次,可通过 -XX reBlockSpin 调整)。
多个线程同时竞争锁(如第三个线程加入竞争)。
过程 :
JVM 将锁升级为重量级锁,对象头指向监视器(Monitor)。
线程进入操作系统内核态的阻塞队列,等待调度器唤醒。
未获取锁的线程通过 ObjectMonitor 等待唤醒。
4. 锁升级的优缺点
4.1 优点
减少无竞争场景的开销 :偏向锁和轻量级锁避免了频繁的 CAS 和上下文切换。
动态适配竞争强度 :在低竞争时保持高性能,在高竞争时保证线程安全。
4.2 缺点
偏向锁撤销开销 :当其他线程竞争时,撤销偏向锁会导致 STW,影响性能。
重量级锁的高开销 :在高竞争场景下,频繁的线程阻塞/唤醒会显著降低性能。
5. 锁升级的优化策略
5.1 减少锁持有时间
优化方向 :缩短同步代码块的执行时间,降低锁的竞争概率。
示例 :// 不推荐:锁持有时间过长 synchronized (lock) { // 复杂计算或 IO 操作 } // 推荐:仅在关键代码块加锁 int result = doSomeComputation(); // 非同步操作 synchronized (lock) { sharedVariable = result; } 复制代码 5.2 使用分段锁(Segment Locking)
优化方向 :将一个大锁拆分为多个小锁,减少锁的竞争范围。
示例 :ConcurrentHashMap 使用分段锁(JDK 8 后改为 CAS + synchronized)。
5.3 禁用偏向锁
适用场景 :频繁切换线程的场景(如高并发服务)。
JVM 参数 :-XX:-UseBiasedLocking # 禁用偏向锁 复制代码 5.4 调整自旋次数
适用场景 :轻量级锁的自旋可能因 CPU 空闲而浪费资源。
JVM 参数 :-XX:PreBlockSpin=5 # 设置自旋次数为 5 复制代码 6. 代码示例
public class LockUpgradeExample { private final Object lock = new Object(); public void performTask() { synchronized (lock) { // 同步代码块 } } public static void main(String[] args) { LockUpgradeExample example = new LockUpgradeExample(); Thread t1 = new Thread(example::performTask); Thread t2 = new Thread(example::performTask); t1.start(); // 初始为偏向锁(t1) t2.start(); // 触发偏向锁撤销,升级为轻量级锁 } } 复制代码 7. 关键 JVM 参数
参数 作用 -XX:+UseBiasedLocking开启/关闭偏向锁(默认开启,Java 15+ 默认关闭)。-XX:BiasedLockingStartupDelay=0立即启用偏向锁(避免延迟)。-XX reBlockSpin设置轻量级锁自旋次数(默认 10)。-XX:-UseSpinning关闭自旋锁(强制进入重量级锁)。8. 总结
锁升级是 JVM 自动管理的机制 ,开发者无需手动干预,但理解其原理有助于优化并发性能。
偏向锁适合单线程场景 ,轻量级锁适合低竞争场景,重量级锁适合高竞争场景。
锁升级不可逆 ,一旦升级到重量级锁,后续操作将始终使用重量级锁。
通过合理设计代码(如减少锁粒度、避免过早膨胀到重量级锁),可以最大化 Java 的并发性能。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
相关推荐