双重校验锁的写法

public class Singleton {

    /*
        volatile 修饰,
        singleton = new Singleton() 可以拆解为3步:
        1、分配对象内存(给singleton分配内存)
        2、调用构造器方法,执行初始化(调用 Singleton 的构造函数来初始化成员变量)。
        3、将对象引用赋值给变量(执行完这步 singleton 就为非 null 了)。
        若发生重排序,假设 A 线程执行了 1 和 3 ,还没有执行 2,B 线程来到判断 NULL,B 线程就会直接返回还没初始化的 instance 了。

        volatile 可以避免重排序。
     */
    private volatile static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

问题2:双重校验锁—为什么使用 volatile 和两次判空校验

1 为什么要进行两次非空校验?

第一个 if 判断是为了减少性能开销

第二个 if 判断是为了避免生成多个对象实例。

2 为什么要用 volatile 关键字?

为了禁止 JVM 的指令重排,指令重排会导致对象未初始化的情况,造成报错