public class Singleton {
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
假设线程A执行到sInstance=new Singleton(),大致做了三件事:
1)给Singleton 的实例分配内存;
2)调用Singleton()的构造函数,初始化成员字段;
3)将sInstance对象指向分配的内存空间(此时sInstance就不是null了)。
但是由于Java编译器允许处理器乱序执行,以及JDK5之前JVM中Cache、寄存器到主内存对写顺序的规定,上面第2和第3的顺序是无法保证的。
如果执行流程是132的话,那么A线程在将3执行完的时候,B线程就直接取走了引用instance(B线程在第二重判断之前)的话,此时取到的引用所指向的对象还未调用构造器初始化成员字段,就会出错。
所以,volatile关键字是为了禁止指令重排序。
第一个判断是否为null,提升效率。如果已经初始化,则不必再争夺锁。
第二个判断是否为null,保证只有一个实例被初始化。