DCL单例模式中,为什么要加volatile?

1,194 阅读2分钟

DCL:double check lock双重检查锁,如下面的代码

public class Singleton {
    private static volatile Singleton INSTANCE = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(INSTANCE==null){//第一次检查
            synchronized (Singleton.class){
                if(INSTANCE==null){//第二次检查
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
为什么要加两次判空,第一次判空能不能不加?

效率问题:假设第一次判空不加,那么每次进入这个方法,INSTANCE不论是不是null,都会执行下面的synchronized代码块,多线程下会出现锁的竞争,而除了第一次初始化,后面的都不会为null,判空的效率比加锁高。

为什么要进行第二次判空?

防止多次初始化:多线程下,有可能会出现两个线程都经过了前面第一次检查,来到了下面的synchronized这里,如果不判空,就会出现一个线程new了一个Singleton出来,然后释放锁,第二个线程进来又会new一个Singleton出来。

volatile能不能不加?

volatile作用:

  1. 保持内存可见性

  2. 防止指令重排序 volatile这里的作用就是防止指令重排INSTANCE = new Singleton();这一行主要做了下面几件事:

  3. 在内存中开辟空间

  4. 执行构造方法初始化对象

  5. 将对象的引用赋值给INSTANCE变量 在不加volatile的情况下,第2和第3步是有可能发生指令重排的,即执行顺序变成了1、3、2,假如我刚好执行到第3步,还没执行第2步,这时候另外一个线程调了这个方法,获取到的是还没执行初始化函数的对象,在上面的代码中,初始化函数什么都没做,所以没什么影响。但是如果初始化函数中需要做一些操作,那就有影响了。


都看到这里了,扫码点个关注呗,持续获取最新文章

qrcode.jpg