環境:
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已經指向了新分配的對象空間。