Java編譯器與雙鎖檢測

環境:

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所引用對象的初始化操作會延後執行,進而增加了其它線程取得未初始對象的可能性。


【解決方案】


保證jre版本在5.0及以上,併爲m_instance 添加volatile關鍵字。volatile關鍵字有兩個效果:一是對所有線程可見;二是禁止指令重排序優化。


【擴展】


由於類似的原因,下面的這種代碼可能會出錯。

objectA = objectA + objectA + objectB;

當編譯器先分配新對象的空間,然後直接將引用賦值給objectA。那麼在之後試圖利用原來的objectA引用的對象來初始化新對象時就會出現問題,因爲此時objectA已經指向了新分配的對象空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章