单例模式DCL实现
pulic class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部声明变量
//volatile防止指令重排
private static volatile Singleton singleton;
//对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
//加入双重检查,解决线程安全问题,同时支持Lazy Loading,同时保证了效率
//推荐使用
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
不加volatile会有什么问题?
程序执行过程中, 为了性能考虑, 编译器和CPU可能会对指令重新排序。
在极少数的情况下,下面语句会发生指令重排
singleton = new Singleton();
以上语句经过编译后,可分为以下三步(伪代码)
memory = allocate(); // 1:分配对象的内存空间
instance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址
以上步骤2与步骤3不存在数据依赖关系,而且无论重排前或者重排后程序的执行结果在单线程中没有变化,因此这种重排优化是允许的。所以存在以下顺序执行情况:
memory = allocate(); // 1:分配对象的内存空间
instance = memory; // 3:设置instance指向刚分配的内存地址
instance(memory); // 2:初始化对象
如果执行顺序为1.3.2,当A线程执行完1,3(此时还未完成对象初始化),这时B线程第一次判断singleton == null,得到的结果未false,然后就会直接return一个空的对象,继而产生后续错误。
为什么需要volatile?
volatile可实现禁止指令重排,防止上述情况发生。