1.定義編程
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。安全
2.適用性多線程
1)當類只能有一個實例而客戶能夠從一個衆所周知的訪問點訪問它時。併發
2)當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。ide
3.結構性能
圖1 單例模式類圖spa
注:線程
Singleton:定義一個Instance操做,容許客戶訪問它的惟一實例。Instance是一個類操做(C#中爲靜態方法),Singleton負責建立它本身的惟一實例。3d
4.實現方法code
1)經典模式
靜態變量uniqueInstance存儲惟一實例。公有靜態方法GetInstance提供訪問SingletonFirst的全局訪問點,因爲除了GetInstance方法外,類的成員均爲私有的,因此GetInstance提供的爲惟一訪問方式。在GetInstance()方法中,對uniqueInstance是否爲null作了判斷,所以使得對象只能被實例化一次。
1 public class SingletonFirst 2 { 3 private static SingletonFirst uniqueInstance; 4 private SingletonFirst() { } 5 6 public static SingletonFirst GetInstance() 7 { 8 if(uniqueInstance==null) 9 { 10 uniqueInstance = new SingletonFirst(); 11 } 12 return uniqueInstance; 13 } 14 }
2)使用屬性而不是方法
這種實現方式與第一種方式的區別在於客戶經過屬性得到對象實例。
1 public class SingletonSecond 2 { 3 private static SingletonSecond uniqueInstance; 4 private SingletonSecond() { } 5 public static SingletonSecond Instance 6 { 7 get 8 { 9 if (uniqueInstance == null) 10 { 11 uniqueInstance = new SingletonSecond(); 12 } 13 return uniqueInstance; 14 } 15 } 16 }
3)只能得到一次單例
這種單例模式的實現方式較以前幾種有較大的差異,客戶不能反覆調用Instance屬性得到實例;客戶要將第一次得到的實例賦給一個變量,以後若要訪問實例,則只能經過使用這一變量的間接方式。
1 public class SingletonThird 2 { 3 private static bool instanceFlag = false; 4 private SingletonThird() { } 5 6 public static SingletonThird Instance 7 { 8 get 9 { 10 if (!instanceFlag) 11 { 12 return new SingletonThird(); 13 } 14 else 15 { 16 return null; 17 } 18 } 19 } 20 }
4)支持併發訪問
這種單例模式的實現方式與第一種基本一致,只不過在聲明uniqueInstanc的同時完成對象的實例化。優勢是靜態字段在類首次被使用前初始化,能夠防止併發訪問。
1 public class SingletonFourth 2 { 3 private static readonly SingletonFourth uniqueInstance = new SingletonFourth(); 4 private SingletonFourth() { } 5 6 public static SingletonFourth GetInstance() 7 { 8 return uniqueInstance; 9 } 10 }
5)使用私有靜態構造器
這種方式使用了私有的靜態構造器。靜態構造器在類第一次被訪問前由CLR自動調用,達到防止併發訪問的目的。使用靜態構造器而不是靜態初始化器的好處是:能夠在靜態構造器中處理異常。
1 public class SingletonFifth 2 { 3 private static readonly SingletonFifth uniqueInstance; 4 static SingletonFifth() 5 { 6 uniqueInstance = new SingletonFifth(); 7 } 8 9 public static SingletonFifth GetInstance() 10 { 11 return uniqueInstance; 12 } 13 }
6)使用鎖
這種方式採用加鎖的辦法來防止併發訪問。缺點就是鎖的開銷比較大,若是用戶對於性能比較關心,那麼不建議採用這種方式。也能夠考慮其餘開銷比較小的同步機制。
1 public class SingletonSixth 2 { 3 private static SingletonSixth uniqueInstance; 4 private static Object lockObj = new object(); 5 private SingletonSixth() { } 6 7 public static SingletonSixth GetInstance() 8 { 9 lock (lockObj) 10 { 11 if (uniqueInstance == null) 12 { 13 uniqueInstance = new SingletonSixth(); 14 } 15 return uniqueInstance; 16 } 17 } 18 }
7)延遲初始化
使用Lazy<T>達到延遲初始化的目的,但這種方式是非線程安全的。
1 public class SingletonSeventh 2 { 3 private static readonly Lazy<SingletonSeventh> lazy = 4 new Lazy<SingletonSeventh>(() => new SingletonSeventh()); 5 private SingletonSeventh(){} 6 7 public static SingletonSeventh Instance 8 { 9 get 10 { 11 return lazy.Value; 12 } 13 } 14 }
8)最簡潔的方式
這種方法與標準的單例模式結構不符,但這種方式的確實現了單例模式, 符合單例模式的定義:只有一個實例且提供一個全局訪問點uniqueInstance靜態變量在聲明時初始化,同時構造器可訪問性爲private,確保類不容許在外部實例化uniqueInstance變量的可訪問性爲public,因此全局的訪問點就是uniqueInstance。
1 public class SingletonEighth 2 { 3 public static readonly SingletonEighth uniqueInstance = new SingletonEighth(); 4 private SingletonEighth() 5 { 6 //這裏執行初始化工做或其餘任務 7 } 8 }
5.擴展單例模式:使類能夠有幾個實例
1 public class SingletonExpansion 2 { 3 private static int count = 0; 4 private static SingletonExpansion uniqueInstance; 5 private SingletonExpansion(){} 6 7 public static SingletonExpansion Instance 8 { 9 get 10 { 11 if (count < 2) 12 { 13 uniqueInstance = new SingletonExpansion(); 14 count++; 15 return uniqueInstance; 16 } 17 else 18 { 19 return uniqueInstance; 20 } 21 } 22 } 23 }
6 概念辨析
1)使用靜態成員與使用單例模式之間的比較:
相同點:
不一樣點:
2)一個類只有惟一的實例不必定就運用了單例模式
單例模式的兩個要素:
上述兩個條件缺一不可,不然就不是單例模式。
7 實現方式總結
1)實現了延遲初始化的爲:SingletonFirst,SingletonSecond,SingletonThird,SingletonSeventh。
延遲初始化的好處:若對象佔用資源(時間上建立時間比較長或空間上耗費內存較大)比較大,或程序的執行過程當中從未使用到,那麼延遲初始化能夠避免耗費資源。若是對象並不佔用較多的資源那麼採用哪一種方式都無所謂了。
2)線程安全的:SingletonFourth,SingletonFifth,SingletonSixth。
多線程編程中其餘幾種實現方法可能會有多個實例。
8 .NET Framework 中的單例模式
1) Microsoft.SqlServer.Server.SmiContextFactory
此方法實現方式與SingletonEighth的實現方式相同
源碼概要
1 namespace Microsoft.SqlServer.Server { 2 3 using System; 4 5 using System.Data.Common; 6 7 using System.Data.SqlClient; 8 9 using System.Diagnostics; 10 11 sealed internal class SmiContextFactory 12 { 13 14 public static readonly SmiContextFactory Instance = new SmiContextFactory(); 15 //省略一些變量 16 private SmiContextFactory() { 17 18 //省略了具體實現} 19 20 //省略其餘方法 21 22 } 23 }
2) sealed internal class SqlConnectionFactory
此方法實現方式與SingletonEighth的實現方式相同
源碼概要
1 namespace System.Data.SqlClient 2 { 3 4 using System; 5 6 using System.Data.Common; 7 8 //省略其餘using 9 10 using Microsoft.SqlServer.Server; 11 sealed internal class SqlConnectionFactory : DbConnectionFactory { 12 private SqlConnectionFactory() : 13 base(SqlPerformanceCounters.SingletonInstance) {} 14 15 public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory(); 16 //省略如下其餘代碼 17 }
3) sealed internal class SqlPerformanceCounters
此方法實現方式與SingletonEighth的實現方式相同
源碼概要
1 namespace System.Data.SqlClient 2 { 3 4 using System; 5 6 using System.Data.Common; 7 8 //省略其餘using語句 9 10 using Microsoft.SqlServer.Server; 11 12 sealed internal class SqlPerformanceCounters : 13 DbConnectionPoolCounters { 14 15 private const string CategoryName = ".NET Data Provider for SqlServer"; 16 17 private const string CategoryHelp = "Counters for System.Data.SqlClient"; 18 19 20 public static readonly SqlPerformanceCounters SingletonInstance = new SqlPerformanceCounters(); 21 22 [System.Diagnostics.PerformanceCounterPermissionAttribute 23 (System.Security.Permissions.SecurityAction.Assert, PermissionAccess=PerformanceCounterPermissionAccess.Write, MachineName=".", CategoryName=CategoryName)] 24 25 private SqlPerformanceCounters() : base (CategoryName, CategoryHelp) { 26 } 27 28 } 29 }