java synchronized偏向锁、轻量级锁、重量级锁
·
要理解 Java 中的偏向锁、轻量级锁、重量级锁,首先要明确:这三种锁是 synchronized 关键字的锁升级机制(JDK 1.6 引入,目的是降低锁的开销),核心是根据竞争程度动态调整锁的形态,从无竞争到高竞争,锁会逐步从偏向锁 → 轻量级锁 → 重量级锁升级(不可逆)。
一、核心背景:为什么需要锁升级?
在 JDK 1.6 之前,synchronized 是纯粹的重量级锁—— 依赖操作系统的互斥量(Mutex)实现,每次加锁 / 解锁都需要切换内核态(用户态 ↔ 内核态),开销极大。
但实际开发中,大部分场景下 synchronized 锁定的代码块竞争非常小(甚至只有一个线程执行),因此 JVM 针对不同竞争程度设计了分级锁:
- 无竞争 → 偏向锁(几乎无开销);
- 轻度竞争 → 轻量级锁(自旋锁,避免内核态切换);
- 重度竞争 → 重量级锁(操作系统互斥量,保证线程安全)。
二、三种锁的详细解析
1. 偏向锁(Biased Locking)
核心特点
- 适用场景:无竞争(只有一个线程反复获取同一把锁);
- 核心思想:锁会 “偏向” 第一个获取它的线程,后续该线程再次获取锁时,无需任何同步操作(连 CAS 都不用),直接执行;
- 实现原理:
- 线程第一次获取偏向锁时,会在对象头的 “Mark Word” 中记录自己的线程 ID;
- 后续该线程进入 / 退出同步块时,只需检查 Mark Word 中的线程 ID 是否为自己:
- 是 → 直接进入(无任何开销);
- 否 → 触发锁升级。
public class BiasedLockDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// 单线程反复获取锁,触发偏向锁
for (int i = 0; i < 10000; i++) {
synchronized (lock) {
// 无其他线程竞争,始终是偏向锁
System.out.println("当前线程:" + Thread.currentThread().getName());
}
}
}
}
关键细节
- 偏向锁默认是延迟启动的(JVM 启动后约 4 秒),可通过 JVM 参数 -XX:-UseBiasedLocking 关闭偏向锁;
- 偏向锁的释放不需要主动操作,只有当其他线程尝试获取锁时,才会撤销偏向锁(触发锁升级)。
2. 轻量级锁(Lightweight Locking)
核心特点
- 适用场景:轻度竞争(多个线程交替获取锁,无长时间阻塞);
- 核心思想:用自旋锁(CAS 操作) 替代操作系统互斥量,避免内核态切换;
- 实现原理:
- 线程获取轻量级锁时,会在自己的栈帧中创建 “锁记录”(Lock Record),并将对象头的 Mark Word 复制到锁记录中;
- 通过 CAS 操作将对象头的 Mark Word 替换为指向锁记录的指针:
- CAS 成功 → 获取锁,执行同步代码;
- CAS 失败 → 线程自旋(循环重试 CAS),自旋次数达到阈值后,升级为重量级锁。
关键细节
- 自旋锁的自旋次数:JDK 1.6 后默认自适应(根据前一次自旋的成功率调整),也可通过 -XX:PreBlockSpin 指定固定次数;
- 轻量级锁的释放:通过 CAS 将对象头的 Mark Word 恢复为原始值,成功则释放锁,失败则说明锁已升级为重量级锁。
3. 重量级锁(Heavyweight Locking)
核心特点
- 适用场景:重度竞争(多个线程同时争抢锁,自旋无法获取);
- 核心思想:依赖操作系统的互斥量(Mutex) 实现,线程获取不到锁时会进入阻塞状态(放弃 CPU);
- 实现原理:
- 锁升级为重量级锁后,对象头的 Mark Word 会指向一个 “监视器(Monitor)” 对象;
- Monitor 维护了一个等待队列,未获取到锁的线程会被放入队列并阻塞;
- 持有锁的线程释放锁时,会唤醒队列中的线程,竞争锁。
关键细节
- 重量级锁的最大开销:线程阻塞 / 唤醒需要切换内核态(用户态 ↔ 内核态),这是一个非常耗时的操作;
- 重量级锁是 synchronized 的 “最终形态”,一旦升级,不会再降级为轻量级锁 / 偏向锁。
三、锁升级的完整流程
用流程图直观展示锁的升级过程:
第一个线程获取锁
四、三种锁的核心对比
| 锁类型 | 适用场景 | 实现方式 | 性能开销 | 线程状态 |
|---|---|---|---|---|
| 偏向锁 | 无竞争(单线程) | 记录线程 ID | ,无同步操作 | 几乎无开销 |
| 轻量级锁 | 轻度竞争(交替获取) | 自旋 + CAS 操作 | 低开销(用户态) | 运行态(自旋,不阻塞) |
| 重量级锁 | 重度竞争(同时争抢) | 操作系统 Mutex+Monitor 队列 | 高开销(内核态) | 阻塞态(放弃 CPU) |
五、实战调优建议
偏向锁调优:
- 适合单线程频繁操作同步块的场景(如单线程循环加锁);
- 如果程序中多线程竞争激烈,可通过 -XX:-UseBiasedLocking 关闭偏向锁(避免偏向锁撤销的开销)。
轻量级锁调优:
- 适合短时间、低竞争的同步块(自旋能快速获取锁);
- 避免同步块执行时间过长(自旋会浪费 CPU 资源)。
重量级锁规避:
- 尽量缩小同步块的范围(仅包裹核心并发逻辑);
- 高并发场景下,可考虑用 java.util.concurrent 包的锁(如 ReentrantLock)替代 synchronized,支持更灵活的锁策略。
总结
- 核心逻辑:synchronized 的锁升级是 “按需升级”,从无竞争到高竞争,锁的开销逐步增大,兼顾了低竞争下的性能和高竞争下的安全性;
- 性能优先级:偏向锁 > 轻量级锁 > 重量级锁;
- 使用原则:根据业务的竞争程度选择(或让 JVM 自动适配),低竞争用偏向 / 轻量级锁,高竞争需优化代码减少锁争抢。
更多推荐




所有评论(0)