最近常常會看一些設計模式上書籍,對設計模式有了必定的瞭解,特此作一下筆記,也給博友們提供一些幫助。數據庫
1.什麼是單例模式?設計模式
網上有不少文章都有詳情的描述,大體意思上能夠理解爲一個類只有一個實例,也就是保證一個類只有一種實例的實現方法,官方上是這樣定義的:確保一個類只有一個實例,並提供一個全局訪問點。多線程
通俗易懂就是:每次去實例化一個對象都是同一個,指向的都是同一個內存地址。併發
2.爲何要使用單例模式,單例模式的好處在哪?性能
每一種設計模式均可以解決開發中遇到的一些問題,好比說提升性能,解耦合,可擴展,功能更加靈活等等。測試
現實中的應用場景: 咱們平常使用的電腦上都有一個回收站,在整個操做系統中,回收站只能有一個實例,整個系統都使用這個惟一的實例,並且回收站自行提供本身的實例。所以回收站是單例模式的應用。平常開發中遇到與數據庫創建鏈接,若是每次都是建立新對象去創建鏈接,速度回很慢,因此設計成單例模式,提升訪問速度。優化
3.單例模式在C#中的具體實現。this
確保一個類只有一個實例,並提供一個全局訪問點。咱們依照這個思路進行實現。spa
本次單例實現總共用了三種方式進行簡單實現:操作系統
方式一:
/// <summary> /// 單例模式的簡單實現 /// </summary> public class Singleton { /// <summary> /// 建立全局靜態變量 /// </summary> private static Singleton _singleton = null; /// <summary> /// 私有化默認構造器,爲何要私有化默認構造器,若是不私有,那麼能夠經過new Singleton()進行實例化對象了,就達不到預想效果。 /// </summary> private Singleton() { } /// <summary> /// 提供一個建立實例的方法 /// </summary> /// <returns></returns> public static Singleton CreatInstance() { //判斷,若是對象沒有建立,那麼進行建立。有建立,不在重複建立 if (_singleton == null) { _singleton=new Singleton(); } return _singleton; } }
測試結果以下:
看測試結果確實達到了咱們預想的要求。單例的效果已經達到了,可是並非很完美,當前只在單線程上是知足了,若是多線程下又是怎麼樣的呢?接下來就用一個簡單的例子測試一下多線程的使用的狀況下。代碼實現以下:
//多線程模擬,十個線程同時訪問 for (int i = 0; i < 10; i++) { new Action(() => { Thread.Sleep(1000); Singleton singleton = Singleton.CreatInstance(); singleton.Show(); //在Singleton類裏面添加一個無參的Show方法 }).BeginInvoke(null, null); }
如上圖發現,當十個線程同時訪問時,建立了多個對象的實例,並沒達到單例模式的效果。爲何會這樣?緣由是這樣的,當第一個線程進入後,
//判斷,若是對象沒有建立,那麼進行建立。有建立,不在重複建立 if (_singleton == null) { _singleton=new Singleton(); }
會去判斷這個對象是否已經被實例化,由於程序執行速度是很快的,當第一個線程發現對象未實例化時,第一個線程開始對對象進行實例化,第一個線程還未實例化完成第二個線程就已經進來了,開始對對象進行判斷,開始實例化。最終形成如上效果。
針對如上狀況,怎麼解決呢?有人就會想到,加把鎖鎖起來就好了。這種想法很是正確。咱們看看加鎖後實現的方式。
public class Singleton { /// <summary> /// 建立全局靜態變量 /// </summary> private static Singleton _singleton = null; private static object singleton_lock=new object(); /// <summary> /// 私有化默認構造器 /// </summary> private Singleton() { Console.WriteLine("建立了Singleton對象的實例"); } /// <summary> /// 提供一個建立實例的方法 /// </summary> /// <returns></returns> public static Singleton CreatInstance() { lock (singleton_lock) { //判斷,若是對象沒有建立,那麼進行建立。有建立,再也不重複建立 if (_singleton == null) { _singleton = new Singleton(); } } return _singleton; } public void Show() { Console.WriteLine($"執行Show方法:{this.GetType()}"); } }
運行後以下圖:
多線程同時訪問的狀況下的問題就已經解決了。固然還能夠再次優化一下,爲了看到效果,對代碼進行以下改造
改造以後再次執行
如上圖發現,每個線程都進入lock裏面,若是第一個線程進入lock實例化了對象,那麼第二個就能夠沒有必要進入lock,咱們能夠在線程進入lock前進行判斷,最終代碼改造以下,
/// <summary> /// 單例模式的簡單實現 /// </summary> public class Singleton { /// <summary> /// 建立全局靜態變量 /// </summary> private static Singleton _singleton = null; private static object singleton_lock=new object(); /// <summary> /// 私有化默認構造器 /// </summary> private Singleton() { Console.WriteLine("建立了Singleton對象的實例"); } /// <summary> /// 提供一個建立實例的方法 /// </summary> /// <returns></returns> public static Singleton CreatInstance() { if (_singleton == null) { lock (singleton_lock) { Console.WriteLine("開始進入lock..."); //判斷,若是對象沒有建立,那麼進行建立。有建立,不在重複建立 if (_singleton == null) { _singleton = new Singleton(); } } } return _singleton; } public void Show() { Console.WriteLine($"執行Show方法:{this.GetType()}"); } }
執行效果以下
圖中發現線程進入lock的次數少了,線程併發少可能做用不大,若是是併發量達到百萬次甚至更多,那麼效率會有明顯提高。雙if加lock是最標準的實現單例模式的寫法。
接下來介紹兩種比較簡便的單例模式的實現,效果和上面的同樣。
方式2、
public class SingletonSecond { private static SingletonSecond _singletonSecond = new SingletonSecond(); private SingletonSecond() { Console.WriteLine("建立了該對象的實例"); } public static SingletonSecond CreatInstance() { return _singletonSecond; } public void Show() { Console.WriteLine($"執行Show方法:{this.GetType()}"); } }
方式3、
public class SingletonThrird { private static SingletonThrird _singletonThrid = null; private SingletonThrird() { Console.WriteLine("建立了該對象的實例"); } static SingletonThrird() { _singletonThrid=new SingletonThrird(); } public static SingletonThrird CreatInstance() { return _singletonThrid; } public void Show() { Console.WriteLine($"執行Show方法:{this.GetType()}"); } }
以上就單例模式的實現方式。提供參考,職場小白歡迎指正!