双重检验的单例模式,为什么要用volatile关键字

双重检验的单例模式是比较推荐的单例写法,在该代码中的单例对象的是用volatile关键字修饰的。这时就产生的一个疑问,为什么需要volatile来修饰呢?
上网查看多个博客,下面简单通俗分析一下当中的原因:
贴上不加volatile单例代码

public class Singleton {
    private static Singleton s;
    private Singleton(){};
    public static Singleton getInstance() {  //1
        if(s == null) { //2
            synchronized (Singleton.class) { //3
                if(s == null) { //4
                    s = new Singleton(); //5
                }
            }
        }
        return s; //6
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

以前不了解为什么需要volatile关键字,后来发现在并发情况下,如果没有volatile关键字,在第5行会出现问题
对于第5行 s = new Singleton(); //5
可以分解为3个步骤:

1 memory=allocate();// 分配内存 相当于c的malloc
2 ctorInstanc(memory) //初始化对象
3 s=memory //设置s指向刚分配的地址

上面的代码在编译器运行时,可能会出现重排序 从1-2-3 排序为1-3-2

如此在多线程下就会出现问题

例如现在有2个线程A,B

线程A在执行第5行代码时,B线程进来,而此时A执行了 1和3,没有执行2,此时B线程判断s不为null 直接返回一个未初始化的对象,就会出现问题

而用了volatile,上面的重排序就会在多线程环境中禁止,不会出现上述问题。
正确双重检验单例模式写法:

public class Singleton {
    private static volatile Singleton s;
    private Singleton(){};
    public static Singleton getInstance() {
        if(s == null) {
            synchronized (Singleton.class) {
                if(s == null) {
                    s = new Singleton();
                }
            }
        }
        return s;
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

总结:加上volatile就是为了防止产生指令的重排序问题
为什么volatile能禁止指令的重排序呢?这里就涉及到volatile的原理了,这里就不多说volatile的原理,可以看看海子大神的博客:volatile关键字解析
---------------------
作者:Tuzki_小辣鸡
来源:CSDN
原文:https://blog.csdn.net/weixin_37659242/article/details/82776198
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2018-11-08 11:12  甜菜波波  阅读(5925)  评论(2编辑  收藏  举报