public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance() {
if(instance == null) { //A线程执行
instance = new Instance(); //B线程执行
}
return instance;
}
public class UnsafeLazyInitialization {
private static Instance instance;
public synchronized static Instance getInstance() {
if(instance == null) { //A线程执行
instance = new Instance(); //B线程执行
}
return instance;
}
public class DoubleCheckedLocking {
private static Instance instance;
public static Instance getInstance() {
if (instance == null) { // 1:第一次检查
synchronized (DoubleCheckedLocking.class) { // 2:加锁
if(instance == null) { // 3:第二次检查
instance = new Instance(); // 4:问题的根源出在这里
}
}
}
return instance;
}
}
如上面代码所示,如果第一次检查instance不为null那么就不需要执行下面的加锁和初始化操作。因此,可以大幅降低synchronized带来的性能开销。但是在线程执行到1的时候,代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化:
示例代码的4(instance = new Instance())创建了一个对象。这一行代码可以分解为如下的三行伪代码。
memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址
上面伪代码中的2和3,可能会被重排序。

解决方案:
1、不允许2和3重排序
用volatile修饰instance,禁止重排序
public class DoubleCheckedLocking {
private volatile static Instance instance;
public static Instance getInstance() {
if (instance == null) { // 1:第一次检查
synchronized ( DoubleCheckedLocking.class) { // 2:加锁
if(instance == null) { // 3:第二次检查
instance = new Instance(); // 4:问题的根源出在这里
}
}
}
return instance;
}
}
2、允许2和3重排序,但不允许其他线程“看到”这个重排序
基于类初始化,JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。(参看初始化类)
public class InstanceFactory {
private static class InstanceHolder{
public static Instance instance = new Instance();
}
public static Instance getInstance() {
return InstanceHolder.instance; //这里InstanceHolder类被初始化
}
}