關於單例模式,你需要知道的幾種寫法

hogen發表於2017-12-29

隨便看看
單例模式在23種設計模式中算是比較常用的了,在應用這個模式時,單例物件的類必須保證只有一個例項存在,而關於它本身也是細分了不同種類的實現(懶漢式、餓漢式、雙重檢查等等),寫法也是7、8種,本篇只是總結了幾種常見的寫法,記得上次面試的時候筆試題就有一道要求寫一個單例出來,權當筆記啦!

這是典型的餓漢式單例,Singleton類載入時就會建立instance例項,這樣就需要提前開銷一部分系統資源。如果該例項在後期未使用,豈不是浪費了系統資源。其它問題不存在

  private Singleton() {}

   private static Singleton instance = new Singleton();

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

懶漢單例模式只有在第一次使用時才會初始化單例,一定程度上能節約資源,但反應會稍慢;通過 synchronized 關鍵字,保證了在多執行緒情況下單例的唯一性,但是在單例被第一次初始化後,再呼叫getInstance() 方法還需要進行同步操作,這樣會造成不必的系統開銷。

 private Singleton(){

   }
   private static Singleton instance;

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

雙重檢查鎖定單例模式(Double Check Lock)在 getInstance() 中,首先對instance進行非空判斷,避免多餘的同步,這也解決了懶漢單例模式中每次同步的問題,接下來如果 instance為空則建立其例項,當然這一步需要保證同步操作。注意 instance = new Singleton();這行程式碼,它的執行可以分解為第三個步驟:

(1)為 instance 例項分配記憶體。

(2)執行 Singleton 建構函式來初始化 instance。

(3)將 instance 指向分配的記憶體。

但在JDK1.5前,上邊的(2)(3)無法保證按順序執行,如果按(1)(3)(2)順序,假如A執行緒執行完(3),(2)未執行就被切換到B執行緒,因為步驟(3)已經在A執行緒執行,則B執行緒直接取走了認為非空 instance,這就導致雙重檢查鎖定的判斷失效。

在JDK1.5後,只要這樣宣告 instance 例項:private volatile static Singleton instance;即新增 volatile 修飾符,這樣就可以保證 instance 每次都從主記憶體讀取,避免了上邊的問題,但會略影響效能。這種單例模式也是在第一次執行 getInstance() 時建立單例,但第一次反映稍慢。

private Singleton() { }
private volatile static Singleton instance;
public static Singleton getInstance(){
   if (instance == null){
       synchronized (Singleton.class){
           if (instance == null){//兩次判斷了instance==null,所以稱為雙重檢查
               instance = new Singleton();
           }
       }
   }
   return instance;
}
複製程式碼

靜態內部類單例模式:這種方式只有在的第一次呼叫getInstance()方法時,虛擬機器才會載入 SingletonHolder 類,並初始化instance 例項,即保證了執行緒同步,也能保證單例的唯一性,相對雙重檢查鎖定單例模式簡單了許多,推薦使用這種方式來實現單例模式。

private Singleton(){}
private static Singleton getInstance(){
    return SingletonHolder.instance;
}
private static class SingletonHolder{
    private static final Singleton instance = new Singleton();
}複製程式碼

相關文章