public class Test9 {
private static volatile Test9 test9;
private Test9(){}
static Test9 getTest9(){
//第一次检测
if(test9 == null){
//加锁
synchronized (Test9.class){
//第二次检测
if(test9 == null){
test9 = new Test9();
}
}
}
return test9;
}
}
其中test9的volatile不可缺少,这里用到的是volatile的特性之一——防止指令重排。
创建对象的过程(new Test9())可以大致分成三步
第一步:分配空间
第二步:初始化
第三步:返回引用
在单线程中,第二三步调换顺序并不会影响结果,因此出于优化的目的,处理器可能把第二步与第三步的执行顺序调换。
但是在多线程中就可能出现问题了。
如果一个线程A正在执行第二次检测中的 test9 = new Test9();语句。此时发生了指令重排,引用返回了(test9不为null)但是test9中的数据还没有初始化完毕。此时又有一个线程B调用了getTest9()方法,并在第一次检测的时候,判断出test9!=null,然后将未初始化完毕的test9返回了。
如果加了volatile后就不会出现这个问题了。