偏向锁是什么?请解释其工作原理和适用场景。
参考回答
偏向锁是 Java 虚拟机(JVM)中的一种锁优化机制,用于减少线程争抢锁时的开销。偏向锁的核心思想是:如果一个锁被一个线程获得,那么该锁会进入“偏向模式”,并倾向于让该线程继续持有锁,而不再进行额外的同步操作。
偏向锁的工作原理
偏向锁是基于 Java 对象头中的锁标志位和线程 ID 实现的。
1. Java 对象头
Java 中的每个对象都包含一个对象头,用于存储锁信息。对象头的结构如下:
- Mark Word:存储锁信息、哈希值或 GC 信息。
- Class Pointer:指向类元数据的指针。
在偏向锁状态下,Mark Word
会存储以下内容:
- 锁标志位:表示当前锁的状态(如偏向锁、轻量级锁、重量级锁)。
- 偏向线程 ID:记录获得锁的线程。
2. 偏向锁的状态流转
偏向锁会经历以下状态:
- 无锁状态:
- 对象初始状态,没有线程持有锁。
- 偏向锁状态:
- 当一个线程第一次获得锁时,JVM 会将对象的锁标志位设置为“偏向锁”,并将当前线程的 ID 写入对象头的
Mark Word
。 - 之后,如果该线程再次获取锁,无需任何同步操作,直接进入临界区。
- 当一个线程第一次获得锁时,JVM 会将对象的锁标志位设置为“偏向锁”,并将当前线程的 ID 写入对象头的
- 偏向锁撤销:
- 如果另一个线程尝试获取偏向锁,JVM 会撤销偏向锁,将其升级为轻量级锁。
- 偏向锁的撤销过程需要停止当前线程和另一个竞争线程,重新分配锁。
- 轻量级锁和重量级锁:
- 偏向锁无法满足高并发场景的需求时,会升级为轻量级锁甚至重量级锁。
3. 偏向锁的优化
- 延迟偏向锁:
- 为了减少偏向锁的创建和撤销开销,JVM 默认会延迟启用偏向锁,直到应用程序运行了一段时间。
- 可通过 JVM 参数
-XX:BiasedLockingStartupDelay=0
禁用延迟。
- 禁用偏向锁:
- 如果偏向锁对性能提升不大,可以通过
-XX:-UseBiasedLocking
参数禁用偏向锁。
- 如果偏向锁对性能提升不大,可以通过
偏向锁的优缺点
优点
- 消除加锁的开销:
- 如果线程反复获取相同的锁,偏向锁可以避免 CAS 操作,直接进入临界区。
- 性能提升:
- 偏向锁特别适合单线程的同步场景,大幅减少锁竞争的开销。
缺点
- 锁撤销开销:
- 偏向锁被撤销时,需要暂停线程并重新分配锁,可能增加性能开销。
- 不适合高并发场景:
- 偏向锁仅适合线程竞争少的场景。在高并发场景下,频繁撤销偏向锁反而会降低性能。
偏向锁的适用场景
适用场景
- 单线程场景:
- 偏向锁最适合只有一个线程反复访问同步代码块的情况,例如线程池中单线程执行任务。
- 低并发场景:
- 偏向锁适用于锁竞争较少的场景,例如初始化阶段或某些批处理任务。
不适用场景
- 高并发场景:
- 偏向锁在高并发下会频繁撤销和升级,反而导致性能下降。
示例:偏向锁的使用
以下代码展示了偏向锁的优化效果:
代码示例
public class BiasedLockExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " acquired the lock");
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " acquired the lock");
}
}
});
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
分析
- 当
t1
首次获取锁时,锁会进入偏向模式。 - 后续的加锁操作由
t1
完成,性能非常高。 - 当
t2
尝试获取锁时,偏向锁会撤销,并升级为轻量级锁。
偏向锁的 JVM 参数
常用 JVM 参数
- 启用/禁用偏向锁:
-XX:+UseBiasedLocking
(默认启用)。-XX:-UseBiasedLocking
(禁用偏向锁)。
- 延迟偏向锁:
-XX:BiasedLockingStartupDelay=0
:立即启用偏向锁。
- 查看锁状态:
- 使用
-XX:+PrintFlagsFinal
查看锁相关的 JVM 参数。 - 使用
jvisualvm
或jconsole
监控锁状态。
- 使用
总结
- 偏向锁的概念: 偏向锁是一种优化机制,通过将锁“偏向”于第一个获取它的线程,避免重复的加锁和解锁操作,提升性能。
- 适用场景: 偏向锁适用于单线程或低并发场景,在高并发下会升级为轻量级锁或重量级锁。
- 核心优势: 偏向锁减少了 CAS 操作和线程调度的开销,显著提高了低竞争场景下的同步性能。
- 使用建议: 在明确场景为高并发时,可以禁用偏向锁;在大部分情况下,默认启用偏向锁是最佳选择。