环境:
jre5
【code】
public class LazySingleton2 {
private static LazySingleton2 m_instance = null;
private LazySingleton2() {
}
public static LazySingleton2 getInstance(){
//threads check instance exist or not
if (m_instance == null){
//block threads,which try to create instance
synchronized(LazySingleton2.class){
//allow only one thread enter at a time
if (m_instance == null){
m_instance = new LazySingleton2();
}
}
}
return m_instance;
}//getInstance
}//class
【问题描述】
在上面的下划线处会出现问题。
无法保证编译器为对象分配内存之后,是直接将对象的引用赋值给m_instance,还是将对象初始化之后才将引用赋值给m_instance。如果是前者,其它线程可能获得没有初始化的对象的引用,这将导致严重的问题。
【原因】
产生问题的可能编译方式:伪代码描述
mem = allocate(); //Allocate memory for Singleton object.
instance = mem; //Note that instance is now non-null, but has not been initialized.other thread may be come at this point.
LazySingleton2<init>(instance); //Invoke constructor for Singleton passing
此外,因为编译器会进行指令重排序优化,上述伪代码之间可以还会插入其它指令。使得mem所引用对象的初始化操作会延后执行,进而增加了其它线程取得未初始对象的可能性。
【解决方案】
【扩展】
由于类似的原因,下面的这种代码可能会出错。
objectA = objectA + objectA + objectB;
当编译器先分配新对象的空间,然后直接将引用赋值给objectA。那么在之后试图利用原来的objectA引用的对象来初始化新对象时就会出现问题,因为此时objectA已经指向了新分配的对象空间。