先说结果:线程不安全
public class Test{ //1
private static Singleton singleton; //2
public static Singleton getSingleton() { //3
//如果第一个线程获取到了单例的实例对象,
//后面的线程再获取实例的时候不需要进入同步代码块中了
if (singleton == null) { //4 第一次检查
// 同步代码块用的锁是单例的字节码文件对象,且只能用这个锁
synchronized (Singleton.class) { //5 加锁
if (singleton == null) { //6 第二次检查
singleton = new Singleton(); //7 问题出现了!
} //8
} //9
} //10
return singleton; //11
}
}
诺,执行到第7行的时候,就是创建对象,
singleton = new Singleton(); 这一行可以分解为如下的3行伪代码:
memory = allocate(); //1,分配对象的内存空间
ctorInstance(memory); //2,初始化对象
instance = memory; //3,设置instance指向刚分配的内存地址
以上三行代码,2,3步可能会被重排序,如果指令重排序之后,发生的情况如下:
memory = allocate(); //1,分配对象的内存空间
instance = memory; //3,设置instance指向刚分配的内存地址;注意!此时对象还没有被初始化!
ctorInstance(memory); //2,初始化对象
重排序之后的执行顺序:
这时候就找到问题了,如果发生指令重排序,线程B有可能在第4行判断singleton不为null,之后线程B开始访问singleton引用的对象,而此时这个对象还没有被A线程初始化。这不就出问题了?
知道了问题,怎么解决是接下来的问题,思路如下:
1,不允许2,3步重排序
2,即使2,3重排序,那这个过程不让其他线程知晓,对外显示为原子操作。
所以懒汉式就需要使用volatile修饰对象