單例模式的雙重檢測問題

單例模式分爲懶漢式和餓漢式兩種,一種是以時間換空間,一種則是以空間換時間,而且餓漢式是具有線程安全,就不必過多討論。我們組要討論爲什麼餓漢式要進行雙重檢測??它又有什麼問題??
新手可能寫出下面的代碼

 private Singleton() { } // 默認構造器  

    private static Singleton instance = null;// 延時加載  
    //每次運行都要創建實例因而需要加鎖保護  
    public static synchronized Singleton getIntance() {  
        if (instance == null) {  
            instance = new Singleton();// 判斷之後加載  
        }  
        return instance;  
    }  

以上可以保證線程安全,但每次都需要獲取鎖,承受同步帶來的性.能開銷.所以我們不需要每次都獲取鎖,只是在創建實例的時候需要而已,下面的代碼再加一重檢測,每次調用函數時再第二次檢測時才考慮取鎖,避免了同步開銷。

public class Singleton {  
 private  static Singleton instance = null;  
 private Singleton() {}  
 public static Singleton getInstance() {  
  if (instance == null) {  
   synchronized (Singleton.class) {// 1  
    if (instance == null) {// 2  
     instance = new Singleton();// 3  
    }  
   }  
  }  
  return instance;  
 }  
}  

上面的代碼還是有些小問題,就是instance = new Singleton()可能出現重排序.如下,我們默認的是

inst = allocat(); // 分配內存  
constructor(inst); // 先執行構造函數
instance = inst;      // 後賦值

但虛擬機可能導致

inst = allocat(); // 分配內存  
instance = inst;      // 先賦值
constructor(inst); // 後執行構造函數

若是第二種,其他線程訪問時,instance不爲null但構造函數沒有完成而導致程序崩潰.所以上面的程序需要加上避免重排的發生,這時就引入了 volatile 關鍵字。

private  volatile static Singleton instance = null; 

參考:
http://jiangzhengjun.iteye.com/blog/652440
http://www.jianshu.com/p/5b2f063d9f68

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