DCL单例模式中,为什么要加volatile?
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步,这时候另外一个线程调了这个方法,获取到的是还没执行初始化函数的对象,在上面的代码中,初始化函数什么都没做,所以没什么影响。但是如果初始化函数中需要做一些操作,那就有影响了。
最近为了督促自己学习,搞了个公众号,算是自己平时的学习笔记,以后面试的时候看一看。有兴趣的可以微信搜索序员说公众号,每天花五分钟跟我一起学习,或者发送消息分享下工作、生活的事情也行。扫下面二维码添加公众号啊!!!