双重检查锁定引发的思考
在学习双重版本版本的单例模式的时候, 我书写了如下代码
1 | public class Singleton { |
这段代码表面上很完美, 但是在高并发的环境下, 容易触发里面一个潜在的BUG, 问题的根源出自于uniqueInstance = new Singleton(); , 因为在线程读取到uniqueInstance不为null的时候, 该变量引用的对象有可能还没有完成初始化。
将uniqueInstance = new Singleton()拆分为以下三行伪代码:1
2
3memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚刚分配的内存空间
在Java语言规范中, 所有线程在执行Java程序是必须要遵守intra-thread semantics, 以保证重排序不会改变单线程内的程序执行结果, 重排序在没有改变单线程程序执行结果的前提下, 可以提高程序的执行性能。
单线程下:
多线程下触发Bug的原因(线程A执行到uniqueInstance = new Singleton(), 线程B执行到第一个if (uniqueInstance == null)):
如果按上面的时序图执行, 线程B有可能会读取到还没有初始化成功的对象。
基于volatile的解决方案
1 | public class Singleton { |
当变量声明为volitle之后,创建该对象的中所触发的指令重排序, 将会在多线程环境中禁止。