目录
前言
在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操作进行自旋,适用于竞争不激烈的场景。
- 重量级锁:使用操作系统同步机制进行阻塞,适用于高竞争的场景。
通过了解锁的升级过程,可以更好地优化并发程序的性能,避免不必要的锁竞争和性能开销。
⭐️ 好书推荐
《Effective Java中文版》
【内容简介】
本书是Jolt获奖作品Effective Java的第3版,对上一版进行了全面更新,涵盖了从Java 5到Java 9的种种特性,是Java开发人员不可缺少的一本参考书。
本书分为12章,包含90个条目,形式简洁。每个条目中都讲述了对Java的独到见解,阐明了如何编写高效、优雅的程序,并且提供了清晰、易懂的示例代码。与上一版相比,本书增加了Lambda表达式、流、Optional类、接口默认方法、try-with-resources、@SafeVarargs注解、模块等Java 7及以后所引入的新特性。本书介绍了如何充分利用泛型、枚举、注解、自动装箱、for-each循环、可变参数、并发机制等各种特性,帮助读者更加有效地使用Java编程语言及其基本类库(java.lang、java.util和java.io,以及子包,如java.util.concurrent和java.util.function等)。
📚 京东购买链接:【2024年全新译本】Effective Java中文版