单例模式 - OK
單例模式(Singleton):保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
一、單例模式
通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,并且它可以提供一個訪問該實例的方法。
單例模式結構圖:
Singleton類,定義一個GetInstance操作,允許客戶訪問它的唯一實例。GetInstance是一個靜態方法,主要負責創建自己的唯一實例。
單例模式示例:
namespace ConsoleApplication1 {public class Singleton{private static Singleton instance;private Singleton() //將構造方法設為private,防止外界利用new創建實例。 { }public static Singleton GetInstance() //此方法用于獲得本類實例的唯一全局訪問點 {if (instance == null) //若實例不存在則new一個新實例,否則返回已有實例 {instance = new Singleton();}return instance;}}class Program{static void Main(string[] args){Singleton singleton1 = Singleton.GetInstance();Singleton singleton2 = Singleton.GetInstance(); //調用靜態方法獲得實例if (singleton1 == singleton2){Console.WriteLine("兩個對象是相同的實例!"); //輸出 兩個對象是相同的示例 }Console.ReadKey();}} }單例模式與實用類(靜態方法)的不同點:
1、實用類不保存狀態,僅提供一些靜態方法或靜態屬性讓你用,而單例是由狀態的。
2、實用類不能用于繼承多態,而單例雖然實例唯一,卻是可以有子類來繼承。
3、實用類只不過是一些方法屬性的集合,而單例卻是有著唯一的對象實例。
二、多線程時的單例
在上面的例子中,如果是在多線程的程序中,多個線程同時(注意是同時)訪問Singleton類,調用GetInstance()方法,會有可能造成創建多個實例的。因此可以通過加lock來處理,lock是確保當一個線程位于代碼的臨界區,另一個線程不進入臨界區。如果其他線程視圖進入鎖定的代碼,則它將一直等待(即被阻止),直到對象被釋放。
雙重鎖定代碼示例:
namespace ConsoleApplication1 {public class Singleton{private static Singleton instance;private static readonly object syncRoot = new object();private Singleton() //將構造方法設為private,防止外界利用new創建實例。 { }public static Singleton GetInstance() //此方法用于獲得本類實例的唯一全局訪問點 {if (instance == null) //如果實例為空,在外面做一層判斷的作用是,當實例不為null時,不用lock,減少資源消耗。 { lock (syncRoot) //鎖定 {if (instance == null) //當兩個線程同時調用GetInstance()時,他們都可以通過第一重instance=null的判斷,然后由于lock機制,//這兩個線程只有一個進入,另一個等候,如果沒有第二重的instance=null的判斷,則第一個線程創建了實例,第二個線程由于過了第一重instance=null,//因此在沒有第二重instance == null判斷的情況下還是會創建實例。 {instance = new Singleton(); }}}return instance;}}class Program{static void Main(string[] args){Singleton singleton1 = Singleton.GetInstance();Singleton singleton2 = Singleton.GetInstance(); //調用靜態方法獲得實例if (singleton1 == singleton2){Console.WriteLine("兩個對象是相同的實例!"); //輸出 兩個對象是相同的示例 }Console.ReadKey();}} }三、靜態初始化
其實在實際應用中,C#與公共語言運行庫也提供了一種‘靜態初始化’方法,這種方法不需要開發人員顯式地編寫線程安全代碼,既可解決多線程環境下它是不安全的問題。相對于上面的來說,兩種方法都可以達到相同的單例目的,只是實現更加簡單而已,來看下實現代碼:
namespace ConsoleApplication1 {public sealed class Singleton //注意必須設置為密封類 {private static readonly Singleton instance = new Singleton(); //注意聲明為只讀private Singleton() {}public static Singleton GetInstance(){return instance;}}class Program{static void Main(string[] args){Singleton singleton1 = Singleton.GetInstance();Singleton singleton2 = Singleton.GetInstance(); //調用靜態方法獲得實例if (singleton1 == singleton2){Console.WriteLine("兩個對象是相同的實例!"); //輸出 兩個對象是相同的示例 }Console.ReadKey();}} }這種實現與前面的示例類似,也是解決了單例模式試圖解決的兩個基本問題:全局訪問和實例化控制,公共靜態屬性為訪問實例提供了一個全局訪問點。不同之處在于它依賴公共語言運行庫來初始化變量。由于構造方法是私有的,因此不能在類本身以外實例化Singleton類;因此,變量引用的是可以再系統中存在的唯一實例。不過要注意,instance變量標記為readonly,這意味著只能在靜態初始化期間或在類的構造函數中分配變量。由于這種靜態初始化的方式是在自己被加載時就將自己實例化,所以被形象地稱之為餓漢單例模式。而上面的第二種是在第一次被引用的時候,才會將自己實例化,所以就被稱之為懶漢單例模式。
由于餓漢式,即靜態初始化的方式,它是類一加載就實例化對象,所以要提前占用系統資源。然而懶漢式,又會面臨著多線程訪問的安全性問題,需要做雙重鎖定這樣的處理才可以保證安全。所以到底使用哪一種方式取決于實際的需求。從C#語言的角度來講,餓漢式的單例模式已經足夠滿足我們的需求。
總結
- 上一篇: 云上救命APP!——e代驾手机客户端!
- 下一篇: Linux性能监控-Top