双重校验锁为什么要用volatile修饰

public class Singleton {  
    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;  
    }  
}

在编写单例模式懒加载时,都会加上volatile修饰,为什么需要加上volatile呢?

这里我们需要提到指令重排序,什么是指令重排序呢?

https://blog.csdn.net/weixin_34037977/article/details/88024601

我们创建对象的时候流程是这样滴

1 加载指定的字节码文件进内存
2 通过new在堆中开辟空间,分配首地址。
3 对对象的属性进行默认初始化。
4 调用与之对应的构造函数,构造函数压栈。
5 构造函数中执行隐式的super()语句,访问父类的构造函数。
6 对对象的属性进行显示初始化。
7 调用类中的构造代码块。
8 执行构造函数中自定义的初始化代码。
9 初始化完毕,将地址赋值给指定的引用。

在重排序后,9就不能保证是在最后一步执行

当两条线程进来时,线程A在执行singleton = new Singleton(),这时线程B在判断singleton == null,因为重排序的原因,他判断singleton不为null。但实际上线程B此时拿到的是一个不完整的对象。

因此我们需要使用volatile来禁止指令重排序优化,从而安全的实现单例。

原文地址:https://www.cnblogs.com/nicori/p/11698240.html