目录
前言
锁的升级过程
1. 偏向锁(Biased Locking)
原理:
示例:
2. 轻量级锁(Lightweight Locking)
原理:
示例:
3. 重量级锁(Heavyweight Locking)
原理:
示例:
注意事项
总结
前言
在Java中,synchronized
关键字用于实现线程同步,确保在同一时间只有一个线程可以访问被保护的代码块。为了提高性能,Java对锁的实现进行了优化,采用了锁的升级机制。锁的升级过程包括从偏向锁、轻量级锁到重量级锁的升级。下面详细讲解锁的升级过程,并提供相应的示例。
锁的升级过程
- 偏向锁(Biased Locking)
- 轻量级锁(Lightweight Locking)
- 重量级锁(Heavyweight Locking)
1. 偏向锁(Biased Locking)
原理:
偏向锁是为了优化只有一个线程访问同步块的情况。默认情况下,对象头中的标记字段(Mark Word)存储了偏向锁的线程ID。只有当另一个线程尝试获取锁时,偏向锁才会升级为轻量级锁。
示例:
public class BiasedLockingExample {private static final Object lock = new Object();public static void main(String[] args) {synchronized (lock) {// 第一个线程获得偏向锁System.out.println("Thread 1 has acquired the biased lock.");}}
}
在这个示例中,synchronized
关键字使得 lock
对象在第一次被线程获取时进入偏向锁状态。
2. 轻量级锁(Lightweight Locking)
原理:
当另一个线程尝试获取已偏向的锁时,偏向锁升级为轻量级锁。轻量级锁使用CAS(Compare-And-Swap)操作来避免阻塞。当竞争不激烈时,轻量级锁的性能优于重量级锁。
示例:
public class LightweightLockingExample {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {synchronized (lock) {// 第一个线程获得偏向锁System.out.println("Thread 1 has acquired the lock.");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {synchronized (lock) {// 第二个线程尝试获取锁,偏向锁升级为轻量级锁System.out.println("Thread 2 has acquired the lock.");}});t1.start();Thread.sleep(100); // 确保t1先获得锁t2.start();t1.join();t2.join();}
}
在这个示例中,第二个线程尝试获取已经被第一个线程偏向的锁,导致偏向锁升级为轻量级锁。
3. 重量级锁(Heavyweight Locking)
原理:
当多个线程频繁竞争同一个锁时,轻量级锁会不断地进行CAS操作,这时轻量级锁会升级为重量级锁(互斥锁)。重量级锁会使用操作系统的同步机制,使得其他线程在获取锁时进入阻塞状态,减少CPU的自旋消耗。
示例:
public class HeavyweightLockingExample {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {synchronized (lock) {// 第一个线程获得锁System.out.println("Thread 1 has acquired the lock.");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {synchronized (lock) {// 第二个线程尝试获取锁,锁升级为重量级锁System.out.println("Thread 2 has acquired the lock.");}});Thread t3 = new Thread(() -> {synchronized (lock) {// 第三个线程尝试获取锁,锁已是重量级锁System.out.println("Thread 3 has acquired the lock.");}});t1.start();Thread.sleep(100); // 确保t1先获得锁t2.start();Thread.sleep(100); // 确保t2在t1之后尝试获取锁t3.start();t1.join();t2.join();t3.join();}
}
在这个示例中,第三个线程尝试获取已经被第二个线程竞争的轻量级锁,导致轻量级锁升级为重量级锁。
注意事项
-
适用场景:
- 偏向锁适用于只有一个线程访问同步块的场景。
- 轻量级锁适用于多个线程交替访问同步块且竞争不激烈的场景。
- 重量级锁适用于多个线程频繁竞争同一个锁的场景。
-
锁升级不可逆:
- 一旦锁从偏向锁升级为轻量级锁或重量级锁,无法降级为偏向锁。为了性能优化,尽量避免频繁的锁竞争。
-
启用和禁用偏向锁:
- 默认情况下,偏向锁是启用的。如果需要禁用,可以在JVM启动参数中添加
-XX:-UseBiasedLocking
。
- 默认情况下,偏向锁是启用的。如果需要禁用,可以在JVM启动参数中添加
总结
Java中 synchronized
锁的升级过程包括从偏向锁、轻量级锁到重量级锁:
- 偏向锁:优化单线程访问的场景,减少不必要的同步开销。
- 轻量级锁:通过CAS操作进行自旋,适用于竞争不激烈的场景。
- 重量级锁:使用操作系统同步机制进行阻塞,适用于高竞争的场景。
通过了解锁的升级过程,可以更好地优化并发程序的性能,避免不必要的锁竞争和性能开销。