https://juejin.cn/post/7325069743143157801
双重校验锁(Double-Checked Locking,简称DCL)是一种多线程设计模式,用于减少同步的开销。双重校验锁在单例模式中的应用是确保只有一个单例实例被创建,同时保持高效的并发访问。
这种模式的核心在于,只有在创建实例时才进行同步,这样一旦实例被创建,就不需要进行同步处理了,因为你只需要在第一次创建对象时同步,以后的访问都不必同步,进而提高了性能。
下面是单例模式中使用双重校验锁的一个例子:
public class Singleton {
// 使用 volatile 关键字确保多线程情况下的可见性和有序性
private volatile static Singleton instance;
private Singleton() {
// 私有构造函数
}
public static Singleton getInstance() {
// 第一次检查:如果实例已经被创建,直接返回已有的实例
if (instance == null) {
// 同步块,只在实例未初始化时进入
synchronized (Singleton.class) {
// 第二次检查:确保只有一个线程能够进入实例创建区块
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这里是如何工作的:
instance
是否已经被实例化,如果已经实例化,则不需要进入同步块,这就是所谓的“懒加载”。instance
是null
,则进入同步块。注意,同步块是同步在Singleton.class
上的,这确保了每次只有一个线程可以进入到创建实例的代码区。instance
是否为 null
,这是因为可能有多个线程同时通过了第一次检查,并等待进入同步块。如果不进行第二次检查,那么每个线程都会创建一个实例,这样就违背了单例模式的原则。volatile
的原因是防止指令重排序。在某些情况下,JVM 可能会对对象的初始化进行优化,导致instance
的引用在对象的所有构造函数执行完毕之前就已经被设置了,如果没有volatile
,其他线程可能访问到一个未完全构造的对象。总之,双重校验锁是一种实现单例模式的高效方法,它在多线程情况下保证了安全性,并且在对象已经创建之后,避免了同步带来的性能开销。然而,需要注意的是,DCL的正确实现在Java中需要使用volatile
关键字,这在Java 5及以上版本才被正确实现。在此之前的Java版本中,可能会由于JVM内存模型的原因导致DCL工作不正确。