你會幾種單例模式的寫法?
單例模式經常會是我們學習設計模式的第一個模式。在spring框架下,Bean的預設初始化也是單例模式。 單例模式常見的有懶漢模式和餓漢模式。 先來說說這兩個命名由來。 懶漢模式: lazily 餓漢模式: early 感覺是音譯。。。
用途
一般情況下,單例物件應該是無狀態的,也就是說沒有成員變數欄位,或者說成員變數不發生變化。 而有狀態的物件,往往在系統中是多例存在。不同的例項擁有不同的狀態。
實現方式
餓漢模式
public class Singleton{ private final static Singleton INSTANCE = new Singleton(); private Singleton(){} public static getInstance(){ return INSTANCE; } } 複製程式碼
優點: 執行緒安全,實現簡單,容易理解。 缺點: 空間浪費。在啟動的時候就會建立例項。
餓漢模式&程式碼塊
public class Singleton { private static Singleton INSTANCE; static { INSTANCE = new Singleton(); } private Singleton(){} public static Singleton getInstance() { return INSTANCE; } } 複製程式碼
這種方式和第一種沒有區別。
懶漢模式
public class Singleton { private staticSingleton INSTANCE; private Singleton(){} public static Singleton getInstance(){ if (INSTANCE == null) { INSTANCE = new Singleton(); } return INSTANCE; } } 複製程式碼
這種方式實現了懶載入,在呼叫例項物件的時候才構造。但是帶來的問題是執行緒不安全。如果是在多執行緒環境下,會生成多個例項。
懶漢模式&同步
public class Singleton { private static Singleton INSTANCE; private Singleton(){} public static synchronized Singleton getInstance() { if (INSTANCE == null) { INSTANCE = new Singleton(); } return INSTANCE; } } 複製程式碼
要解決執行緒安全問題,最簡單的做法是讓方法加個同步。這樣一來便可保證執行緒安全,但是帶來的問題是效能損耗。因為其實只有在第一次構造的時候需要同步,以後獲取的時候不需要同步的。
懶漢模式&double check
public class Singleton { private static Singleton INSTANCE; private Singleton(){} public staticSingleton getInstance() { if (INSTANCE != null) { return INSTANCE; } synchronized (Singleton.class) { if (INSTANCE == null) { INSTANCE = new Singleton(); } } return INSTANCE; } } 複製程式碼
這種方式就比較完美了。做一個雙重檢查,保證了在第一次構造例項的時候的執行緒安全。同時也保證了之後獲取例項物件不需要同步。
懶漢模式&內部類
public class Singleton { private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } private Singleton(){} public static Singleton getInstance() { return SingletonInstance.INSTANCE; } } 複製程式碼
這種方式表面上看和餓漢模式很相似,但是其實也是實現了懶載入。巧妙之處在於利用了內部類啟動機制保證了執行緒安全。個人比較推薦以上兩種寫法。
列舉用法
public enum Singleton { INSTANCE; } 複製程式碼
坊間還有使用列舉類來實現的例子,雖然也能達到單例的效果,同時也是執行緒安全。但是個人並不推崇這種用法,原因在於列舉並不是設計來實現單例的,理解起來會讓人困惑。