單例模式:對於類的單例模式設計,就是採取一定的方法保證在整個軟體系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。 單例模式有8種方式: 1、餓漢式(靜態常量) // 2、餓漢式(靜態代碼塊) 3、懶漢式(線程不安全) 4、懶漢式(線程安全,同步方法) 5、懶 ...
單例模式:對於類的單例模式設計,就是採取一定的方法保證在整個軟體系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。
單例模式有8種方式:
1、餓漢式(靜態常量)
// 2、餓漢式(靜態代碼塊)
3、懶漢式(線程不安全)
4、懶漢式(線程安全,同步方法)
5、懶漢式(線程安全,同步代碼塊)
6、雙重檢查double check
7、靜態內部類
// 8、枚舉
★餓漢式(靜態常量)
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton instance1 = Singleton.getInstance(); 7 Singleton instance2 = Singleton.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton 16 { 17 /// <summary> 18 /// 1、私有化構造函數,外部不能new 19 /// </summary> 20 private Singleton() 21 { 22 } 23 24 // 2、本類的內部創建對象實例 25 private readonly static Singleton instance = new Singleton(); 26 27 // 3、提供一個公有的靜態方法,返回對象實例 28 public static Singleton getInstance() 29 { 30 return instance; 31 } 32 }view code
優缺點說明:
1、優點:寫法簡單,就是在類狀態的時候就完成了實例化,避免線程同步問題。
2、缺點:在類狀態的時候完成實例化,沒有達到lazy loading的效果。如果從始至終從未使用過這個實例,則會造成記憶體的浪費。
3、這種方式基於classloader機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,在單例模式中大多數都是調用getInstance方法,但是導致類裝載的原因有很多,因此不能確定有其他的方式導致裝載,這時候初始化instance就沒有達到lazy loading的效果。
結論:這種單例模式可用,可能造成記憶體浪費。
★餓漢式(靜態代碼塊)
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton instance1 = Singleton.GetInstance; 7 Singleton instance2 = Singleton.GetInstance; 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton 16 { 17 /// <summary> 18 /// 1、私有化構造函數,外部不能new 19 /// </summary> 20 private Singleton() 21 { 22 } 23 24 public static Singleton GetInstance { get; private set; } = new Singleton(); 25 }view code
★懶漢式(線程不安全)
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton1 instance1 = Singleton1.getInstance(); 7 Singleton1 instance2 = Singleton1.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton1 16 { 17 // 私有的靜態變數 18 private static Singleton1 instance; 19 20 /// <summary> 21 /// 1、私有化構造函數,外部不能new 22 /// </summary> 23 private Singleton1() 24 { 25 } 26 27 /// <summary> 28 /// 提供一個靜態的公有方法,當使用到該方法時,才去創建instance,即懶漢式 29 /// </summary> 30 /// <returns></returns> 31 public static Singleton1 getInstance() 32 { 33 if (instance == null) 34 { 35 instance = new Singleton1(); 36 } 37 return instance; 38 } 39 }view code
優缺點說明:
1、起到了lazy loading效果,但是只能在單線程下使用。
2、若在多線程下,一個線程進入if(singleton==null)判斷語句塊,還未來得及往下執行,另一個線程也通過了該判斷,就會產生多個實例。所以在多線程環境下不可使用這種方式。
結論:在實際開發中,不要使用這種方式。
★懶漢式(線程安全,同步方法)
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton3 instance1 = Singleton3.getInstance(); 7 Singleton3 instance2 = Singleton3.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton3 16 { 17 // 定義一個靜態變數來保存類的實例 18 private static Singleton3 instance; 19 20 // 定義一個標識確保線程同步 21 private static readonly object locker = new object(); 22 23 /// <summary> 24 /// 私有化構造函數,外部不能new 25 /// </summary> 26 private Singleton3() 27 { 28 } 29 30 /// <summary> 31 /// 提供一個靜態的公有方法,當使用到該方法時,才去創建instance,即懶漢式 32 /// </summary> 33 /// <returns></returns> 34 public static Singleton3 getInstance() 35 { 36 // 當第一個線程運行到這裡時,此時會對locker對象 "加鎖", 37 // 當第二個線程運行該方法時,首先檢測到locker對象為"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 38 // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖" 39 lock (locker) 40 { 41 if (instance == null) 42 { 43 instance = new Singleton3(); 44 } 45 } 46 return instance; 47 } 48 }view code
優缺點說明:
1、解決了線程不安全的問題。
2、效率太低,每個線程都想獲取類實例的時候,執行getInstance()時都需要進行同步,而該方法只需要執行一次實例化代碼就夠了,後面想獲取該類的實例直接return就可以了。
結論:在實際開發中,不推薦使用這種方式。
★懶漢式(線程安全,同步代碼塊)
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton4 instance1 = Singleton4.getInstance(); 7 Singleton4 instance2 = Singleton4.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton4 16 { 17 // 定義一個靜態變數來保存類的實例 18 private static Singleton4 instance; 19 20 // 定義一個標識確保線程同步 21 private static readonly object locker = new object(); 22 23 /// <summary> 24 /// 私有化構造函數,外部不能new 25 /// </summary> 26 private Singleton4() 27 { 28 } 29 30 /// <summary> 31 /// 提供一個靜態的公有方法,當使用到該方法時,才去創建instance,即懶漢式 32 /// </summary> 33 /// <returns></returns> 34 public static Singleton4 getInstance() 35 { 36 if (instance == null) 37 { 38 lock (locker) 39 { 40 instance = new Singleton4(); 41 } 42 } 43 return instance; 44 } 45 }view code
優缺點說明:
1、這種方式本意上時對上述方式的改進,改為同步產生實例化的代碼塊。
2、但是這種方式沒有解決線程安全問題。
結論:強烈不推薦。
★雙重檢查
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton5 instance1 = Singleton5.getInstance(); 7 Singleton5 instance2 = Singleton5.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton5 16 { 17 // 定義一個靜態變數來保存類的實例 18 private static Singleton5 instance; 19 20 // 定義一個標識確保線程同步 21 private static readonly object locker = new object(); 22 23 /// <summary> 24 /// 私有化構造函數,外部不能new 25 /// </summary> 26 private Singleton5() 27 { 28 } 29 30 /// <summary> 31 /// 提供一個靜態的公有方法,當使用到該方法時,才去創建instance,即懶漢式 32 /// </summary> 33 /// <returns></returns> 34 public static Singleton5 getInstance() 35 { 36 // 當第一個線程運行到這裡時,此時會對locker對象 "加鎖", 37 // 當第二個線程運行該方法時,首先檢測到locker對象為"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 38 // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖" 39 // 雙重鎖定只需要一句判斷就可以了 40 if (instance == null) 41 { 42 lock (locker) 43 { 44 if (instance == null) 45 { 46 instance = new Singleton5(); 47 } 48 } 49 } 50 return instance; 51 } 52 }view code
結論:推薦使用。
★靜態內部類
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 測試 6 Singleton2 instance1 = Singleton2.getInstance(); 7 Singleton2 instance2 = Singleton2.getInstance(); 8 9 Console.WriteLine(instance1 == instance2); 10 Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}"); 11 Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}"); 12 } 13 } 14 15 internal class Singleton2 16 { 17 /// <summary> 18 /// 1、私有化構造函數,外部不能new 19 /// </summary> 20 private Singleton2() 21 { 22 } 23 24 private static class SingletonInstance 25 { 26 public readonly static Singleton2 INSTANCE = new Singleton2(); 27 } 28 29 /// <summary> 30 /// 提供一個靜態的公有方法,當使用到該方法時,才去創建instance,即懶漢式 31 /// </summary> 32 /// <returns></returns> 33 public static Singleton2 getInstance() 34 { 35 return SingletonInstance.INSTANCE; 36 } 37 }view code
優缺點說明:
1、這種方式採用了類裝載的機制來保證初始化實例時只有一個線程。
2、靜態內部類方式在Singleton類被裝載時並不會立即實例化,而是在需要實例化時,通過getInstance方法才會裝載SingletonInstance類,從而完成Singleton實例化。
3、類的靜態屬性只會在類第一次裝載的時候初始化。
4、避免了線程不安全,利用靜態內部類特點實現延遲載入,效率高。
結論:推薦使用。
★枚舉
在java中提供該方式。
C#中實現了單例模式的類:
1 // 該類不是一個公開類 2 // 但是該類的實現應用了單例模式 3 internal sealed class SR 4 { 5 private static SR loader; 6 internal SR() 7 { 8 } 9 // 主要是因為該類不是公有,所以這個全部訪問點也定義為私有的了 10 // 但是思想還是用到了單例模式的思想的 11 private static SR GetLoader() 12 { 13 if (loader == null) 14 { 15 SR sr = new SR(); 16 Interlocked.CompareExchange<SR>(ref loader, sr, null); 17 } 18 return loader; 19 } 20 21 // 這個公有方法中調用了GetLoader方法的 22 public static object GetObject(string name) 23 { 24 SR loader = GetLoader(); 25 if (loader == null) 26 { 27 return null; 28 } 29 return loader.resources.GetObject(name, Culture); 30 } 31 }view code
參考:https://www.cnblogs.com/zhili/p/SingletonPatterm.html