單例模式(Singleton):保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。javascript
一般咱們可讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類自身負責保存它的惟一實例。這個類能夠保證沒有其餘實例能夠被建立,而且它能夠提供一個訪問該實例的方法。java
單例模式結構圖:安全
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(); } } }
單例模式與實用類(靜態方法)的不一樣點:post
一、實用類不保存狀態,僅提供一些靜態方法或靜態屬性讓你用,而單例是由狀態的。this
二、實用類不能用於繼承多態,而單例雖然實例惟一,倒是能夠有子類來繼承。spa
三、實用類只不過是一些方法屬性的集合,而單例倒是有着惟一的對象實例。線程
在上面的例子中,若是是在多線程的程序中,多個線程同時(注意是同時)訪問Singleton類,調用GetInstance()方法,會有可能形成建立多個實例的。所以能夠經過加lock來處理,lock是確保當一個線程位於代碼的臨界區,另外一個線程不進入臨界區。若是其餘線程視圖進入鎖定的代碼,則它將一直等待(即被阻止),直到對象被釋放。code
雙重鎖定代碼示例:
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#語言的角度來說,餓漢式的單例模式已經足夠知足咱們的需求。