双重校验锁为什么要用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来禁止指令重排序优化,从而安全的实现单例。