如何寫出一個好的單例模式

綾曉路發表於2019-01-23

單例模式是設計模式中總容易理解,並且使用次數比較多的模式,往往在面試中會被問到。在這裡我簡述下自己的思路。希望能對你有所幫助。 ###懶漢式 單例模式

public class Single {
    private static Single instance;
    private Single () {}
    
    public static Single getInstance () {
      if (instance == null) {
        instance = new Single();
    }
  return instance;
  }
}
複製程式碼

上面這種,是我們眾所周知的,往往教科書上也是這麼寫的,但是這種是執行緒不安全的,那麼我們來看下一個。

懶漢式2

public static synchronized Single getInstance() {
  if (instance == null) {
    instance = new Single(); 
  }
return instance;
}
複製程式碼

這種相對第一種來說,就安全了很多,但是它並不高效。

雙重檢驗鎖

雙重檢驗鎖模式(double checked locking pattern),是一種使用同步塊加鎖的方法。被稱其雙重檢驗鎖。是因為會有兩次檢驗instance == null,一次是在同步塊外,一次是在同步塊內。

public static Single getInstance () {
  if(instance == null) {
    synchronized (Single.class) {
      if(instance ==null) {
        instance = new Single();
      }
    }
   }
  return instance;
}
複製程式碼

這樣是不是很完美了,可是還有問題,我們來看下 JVM 對instance = new Single()這句話做了什麼

  • 大概做了三件事: 1.給instance分配記憶體 2.呼叫Single的建構函式來初始化成員變數 3.將instance物件指向給分配的記憶體 JVM的即時編譯器中存在指令重排序的優化。這樣就會導致上邊的第二步和第三步的實行順序不確定。最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是後者,則在 3 執行完畢、2 未執行之前,被執行緒二搶佔了,這時 instance 已經是非 null 了(但卻沒有初始化),所以執行緒二會直接返回 instance,然後使用,然後順理成章地報錯。
public class Single() {
   private volatile static Single instance; // 宣告成 volatile
   private Single () {}
   public static Single getInstance () {
     if (instance == null) {
       synchronized (Single.class) {
           if(instance == null) {
             instance = new Single();
           }
       }
     }
     return instance;
   }
}
複製程式碼

餓漢式 static final field

public class Single{
    //類載入時就初始化
    private static final Single instance = new Single();
    
    private Single(){}

    public static Single getInstance(){
        return instance;
    }
}
複製程式碼

靜態內部類 static nested class

public class Single {  
    private static class SingleHolder {  
        private static final Single INSTANCE = new Single();  
    }  
    private Single (){}  
    public static final Single getInstance() {  
        return SingleHolder.INSTANCE; 
    }  
}
複製程式碼

列舉 Enum

用列舉寫單例實在太簡單了!這也是它最大的優點。下面這段程式碼就是宣告列舉例項的通常做法。

public enum EasySingle{
    INSTANCE;
}
複製程式碼

相關文章