DCL:Double Check Lock(双重检查锁)

public static Singleton getInstance() {
    // 一次检查(非同步)。
    if (Objects.isNull(singleton)) {
        synchronized (Singleton.class) {
            // 二次检查(同步)。
            if (Objects.isNull(singleton)) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

双重检查锁(DCL)的失效原因

在jvm底层new Singleton()时其实执行了三行代码:

1
2
3
4
//伪代码
memory = allocategories();//分配空间
ctorInstance(memory) //初始化对象
singleton = memory //设置singleton指向分配的内存地址

第二行和第三行可能会被重排序:

1
2
3
4
//伪代码
memory = allocategories();//分配空间
singleton = memory //设置singleton指向分配的内存地址
ctorInstance(memory) //初始化对象

问题出现在 先指向分配的内存地址,然后在初始化属性,当其他的线程来访问这个对象的时候很大可能访问到一个仅仅是被分配了内存,但并未完成初始化的不完整对象,那么DCL就失效了

避免双重检查锁(DCL)失效

  1. volatile
  2. 基于类初始化解决

JVM在类的初始化阶段(即Class被加载后,且被线程使用之前),会执行类的初始化阶段,JVM会获取一个锁。这个锁可以同步多个线程对同一个类的初始化。