設計模式-建立型-單例模式
單例模式:對於類的單例模式設計,就是採取一定的方法保證在整個軟體系統中,對某個類只能存在一個物件例項,並且該類只提供一個取得其物件例項的方法(靜態方法)。
單例模式有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 }
優缺點說明:
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 }
★懶漢式(執行緒不安全)
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.