
public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance() {
if (instance == null)
instance = new Instance();
return instance;
}
}
在单线程的情况下这种方式是一个很好的选择,但是再多线程情况下,实例为空的判断和初始化实例这部分临界区存在线程安全的问题
对于多线程的模式下为了保证线程安全下可以使用 java synchronized关键字,保证竞态资源的访问。
public class SafeLazyInitialization {
private static Instance instance;
public synchronized static Instance getInstance() {
if (instance == null)
instance = new Instance();
return instance;
}
}
上述的代中对 getInstance 加锁,防止线程不安全问题,在 instance 对象访问量较少的情况下是一个很好的选择,但是如果 instance 已经初始化完成,此时就没有必要使用锁,增加了不必要的开销。
public class DoubleCheckedLocking {
private static Instance instance;
public static Instance getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLocking.class) {
if (instance == null)
instance = new Instance();
}
}
return instance;
}
}
利用双重检查锁定来降低锁的开销,看起来似乎很完美,但是这是一个错误的优化,问题就出现在下面这一行代码中
代码读 取到instance不为null时,instance引用的对象有可能还没有完成初始化。
instance = new Instance();问题原因
创建一个对象 instance = new Instance(); 可以分为下面三个步骤 1.开辟内存空间 memory = allocate(); // A 2.对象初始化 ctorInstance(memory); // B 3.设置instance指向刚分配的内存地址 instance = memory; //C
这里的 A、B、C 可能存在重排序的问题 ,以为 A happens-before B 、A happens-before C ,但是 B、C 没有依赖关系,所以初始化对象的执行顺序可能是 A-B-C 或者 A-C-B, 当执行顺序为 A-C-B 时 就存在 当 instance 不为 null 时,instance 没有初始化完成。
解决方案public class SafeDoubleCheckedLocking {
private volatile static Instance instance;
public static Instance getInstance() {
if (instance == null) {
synchronized (SafeDoubleCheckedLocking.class) {
if (instance == null)
instance = new Instance();
}
}
return instance;
}
}
public class InstanceFactory {
private static class InstanceHolder {
public static Instance instance = new Instance();
}
public static Instance getInstance() {
return InstanceHolder.instance ; // 这里将导致InstanceHolder类被初始化
}
}