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作用:
-
保持内存可见性
-
防止指令重排序 volatile这里的作用就是防止指令重排,
INSTANCE = new Singleton();
这一行主要做了下面几件事: -
在内存中开辟空间
-
执行构造方法初始化对象
-
将对象的引用赋值给INSTANCE变量 在不加volatile的情况下,第2和第3步是有可能发生指令重排的,即执行顺序变成了1、3、2,假如我刚好执行到第3步,还没执行第2步,这时候另外一个线程调了这个方法,获取到的是还没执行初始化函数的对象,在上面的代码中,初始化函数什么都没做,所以没什么影响。但是如果初始化函数中需要做一些操作,那就有影响了。
都看到这里了,扫码点个关注呗,持续获取最新文章