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;
    }
}

这里是如何工作的:

  1. 第一次检查:检查 instance 是否已经被实例化,如果已经实例化,则不需要进入同步块,这就是所谓的“懒加载”。
  2. 同步块:如果 instancenull,则进入同步块。注意,同步块是同步在Singleton.class上的,这确保了每次只有一个线程可以进入到创建实例的代码区。
  3. 第二次检查:在同步块内再次检查 instance 是否为 null ,这是因为可能有多个线程同时通过了第一次检查,并等待进入同步块。如果不进行第二次检查,那么每个线程都会创建一个实例,这样就违背了单例模式的原则。
  4. 实例化:如果第二次检查仍然是 null,则创建 Singleton 的新实例。
  5. volatile关键字:使用volatile的原因是防止指令重排序。在某些情况下,JVM 可能会对对象的初始化进行优化,导致instance的引用在对象的所有构造函数执行完毕之前就已经被设置了,如果没有volatile,其他线程可能访问到一个未完全构造的对象。

总之,双重校验锁是一种实现单例模式的高效方法,它在多线程情况下保证了安全性,并且在对象已经创建之后,避免了同步带来的性能开销。然而,需要注意的是,DCL的正确实现在Java中需要使用volatile关键字,这在Java 5及以上版本才被正确实现。在此之前的Java版本中,可能会由于JVM内存模型的原因导致DCL工作不正确。

单例模式双重校验锁写法双重检查的作用