保證一個類只有一個實例,並提供一個全局變量來訪問這個實例,這就是單例模式,單例模式屬於對象建立型模式。安全
class Singleton { private static Singleton s_Instance; //私有構造方法,防止外部實例化 private Singleton() { } public static Singleton GetInstance() { if (s_Instance == null) s_Instance = new Singleton(); return s_Instance; } }
測試代碼多線程
static void Main() { var s1 = Singleton.GetInstance(); var s2 = Singleton.GetInstance(); if (object.ReferenceEquals(s1, s2)) Console.WriteLine($"s1 s2兩個實例是相同的"); Console.ReadKey(); }
結果
併發
class EagerSingleton { //類加載時執行 private static EagerSingleton s_Instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton GetInstance() { return s_Instance; } }
//懶漢式單例與餓漢式單例不一樣的在於:懶漢式單例在第一次被引用時將本身實例化(類被加載時不會實例化自身) class LazySingleton { private static LazySingleton s_Instance; //私有構造方法,防止外部實例化 private LazySingleton() { } public static LazySingleton GetInstance() { if (s_Instance == null) s_Instance = new LazySingleton(); return s_Instance; } }
在高併發多線程的環境下運行懶漢式單例的代碼,會出如今某一時刻存在多個線程同時訪問GetInstance方法,可能會建立多個實例,違背了單例模式的設計意圖。高併發
修改懶漢式單例代碼使其線程安全性能
class LazySingleton { private static readonly object s_LockObj = new object(); private static LazySingleton s_Instance; //私有構造方法,防止外部實例化 private LazySingleton() { } public static LazySingleton GetInstance() { if (s_Instance == null) { //加鎖使程序在某一時刻只容許一個線程訪問 lock (s_LockObj) { if (s_Instance == null) s_Instance = new LazySingleton(); } } return s_Instance; } }
當實例不存在,而且多個線程同時調用GetInstance方法,此時它們都能經過第一重"s_Instance == null"的判斷,此時只會有一個線程進入lock塊執行建立代碼,若是不進行第二次的"s_Instance == null"判斷,當第一個線程建立完實例離開後,第二個線程進入lock塊,因爲第二個線程並不知道實例已經被建立,將繼續建立新的實例,仍是會產生多個實例對象。測試
餓漢式單例在類被加載時就建立實例對象,優勢在於無需考慮線程安全問題,因爲單例對象一開始就建立好了,因此調用速度優於懶漢式單例。同時,由於類加載時就須要建立單例對象,所以從資源利用角度來講,餓漢式單例不如懶漢式單例,類的加載時間相對懶漢式單例也更慢。spa
懶漢式單例在第一次使用時建立單例對象,實現了延遲加載,無須一直佔用系統資源。可是懶漢式單例必須處理線程安全問題,這將致使系統性能受到必定程度的影響。線程