Java:單例模式我只推薦兩種

雙重檢查模式

public class Singleton {  
    private volatile static Singleton singleton;  //1:volatile修飾
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  //2:減小不要同步,優化性能
        synchronized (Singleton.class) {  // 3:同步,線程安全
        if (singleton == null) {  
            singleton = new Singleton();  //4:建立singleton 對象
        }  
        }  
    }  
    return singleton;  
    }  
}
複製代碼

推薦理由:安全

  1. 延遲初始化。和懶漢模式一致,只有在初次調用靜態方法getSingleton,纔會初始化signleton實例。
  2. 性能優化。同步會形成性能降低,在同步前經過判讀singleton是否初始化,減小沒必要要的同步開銷。
  3. 線程安全。同步建立Singleton對象,同時注意到靜態變量singleton使用volatile修飾。

爲何要使用volatile修飾?性能優化

雖然已經使用synchronized進行同步,但在第4步建立對象時,會有下面的僞代碼:bash

memory=allocate(); //1:分配內存空間
ctorInstance();   //2:初始化對象
singleton=memory; //3:設置singleton指向剛排序的內存空間
複製代碼

當線程A在執行上面僞代碼時,2和3可能會發生重排序,由於重排序並不影響運行結果,還能夠提高性能,因此JVM是容許的。若是此時僞代碼發生重排序,步驟變爲1->3->2,線程A執行到第3步時,線程B調用getsingleton方法,在判斷singleton==null時不爲null,則返回singleton。但此時singleton並還沒初始化完畢,線程B訪問的將是個還沒初始化完畢的對象。當聲明對象的引用爲volatile後,僞代碼的二、3的重排序在多線程中將被禁止!markdown

靜態內部類模式

public class Singleton { 
    private Singleton(){
    }
      public static Singleton getSingleton(){  
        return Inner.instance;  
    }  
    private static class Inner {  
        private static final Singleton instance = new Singleton();  
    }  
} 
複製代碼

推薦理由:多線程

  1. 實現代碼簡潔。和雙重檢查單例對比,靜態內部類單例實現代碼真的是太簡潔,又清晰明瞭。
  2. 延遲初始化。調用getSingleton才初始化Singleton對象。
  3. 線程安全。JVM在執行類的初始化階段,會得到一個能夠同步多個線程對同一個類的初始化的鎖。

如何實現線程安全?性能

線程A和線程B同時試圖得到Singleton對象的初始化鎖,假設線程A獲取到了,那麼線程B一直等待初始化鎖。線程A執行類初始化,就算雙重檢查模式中僞代碼發生了重排序,也不會影響線程A的初始化結果。初始化完後,釋放鎖。線程B得到初始化鎖,發現Singleton對象已經初始化完畢,釋放鎖,不進行初始化,得到Singleton對象。優化

在涉及到反射和序列化的單例中,建議使用下文的枚舉類型模式。spa

其餘類型的單例模式

懶漢模式(多線程不安全)

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}
複製代碼

餓漢單例模式(多線程安全)

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}
複製代碼

餓漢模式的線程安全一樣經過類加載解決同步問題,但沒有達到懶加載目的。(這裏很是感謝之初z-chu的指正)線程

枚舉單例模式(多線程安全)

public enum Singleton {
    INSTANCE;
    
    public void doSomething(){
        //todo doSomething
    }
}
複製代碼

在Joshua Bloch大神的《Effective Java》是推薦該方法的。雖然線程安全,在實際開發中,尚未被普遍採用。由於太過簡潔以至於可讀性較差,尚未在實戰中被普遍推廣。枚舉單例模式的線程安全一樣利用靜態內部類中講到類初始化鎖。枚舉單例模式可以在序列化和反射中保證明例的惟一性。code

高手之間的過招,必選擇枚舉單例模式

點個贊,老鐵
若是以爲文章有用,給文章點個贊,鐵子
相關文章
相關標籤/搜索