爲何將單例模式排名第一,很簡單,面試的時候聊到設計模式,大機率就從單例模式開始入手,循循漸進。面試
固然,咱們學習單例模式不是爲了去應付面試,而是學好設計模式後,融匯貫通,應用於咱們的設計,開發,項目中。數據庫
單例模式是最簡單的設計模式之一設計模式
單例模式【Singleton Pattern】:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。緩存
兩層含義:1,有且僅有一個實例安全
2,有一個訪問他的全局訪問點多線程
咱們來想象一個場景,放眼全球,全部的黨派,都只有一個主席(這應該沒有例外吧)這個案例,若是要拜訪他,是否是須要一個全局的訪問點。ide
對於一個類來講,咱們怎樣保證他僅有一個實例,初步想到的是不許外部實例化,由於若是能夠外部實例化,我就能夠實例化無數個。那不許外部實例化,就只能內部了,那就動手修改內部的構造方法。函數
(敲黑板,劃重點) 全部類都有構造方法,不編碼則系統默認生成空的構造方法,如有顯示定義的構造方法,默認的構造方法就會失效學習
如此,咱們只須要顯示的修改構造方法便可,既然不許外部調用,那咱們用private修飾構造方法便可優化
第一步,控制外部實例化咱們就作到了
/// <summary> /// 黨派 /// </summary> public class Party { /// <summary> /// 私有化構造函數,不許外部實例化 /// </summary> private Party() { } } }
那咱們須要一個主席,一個黨派總不能羣龍無首吧,因而咱們定義一個主席,而且咱們要提供一個訪問的點吧,否則別人想和主席聊兩句都找不到人
第二步,建立一個主席,並提供一個訪問點
/// <summary> /// 黨派 /// </summary> public class Party { /// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman; /// <summary> /// 私有化構造函數,不許外部實例化 /// </summary> private Party() { } /// <summary> /// 找主席 /// </summary> /// <returns></returns> public static Party GetChairman() { if (_chairman == null)//主席不存在則建立一個主席 { _chairman = new Party(); } return _chairman; } } }
哇哇哇~!!!私有構造函數,爲何這裏能夠調用啊,不是不讓調用麼!!!這是同一個類中,同一個類中,私有,公有,保護,均可以隨意調用。
咱們再讓主席講一句話
/// <summary> /// 黨派 /// </summary> public class Party { /// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman; /// <summary> /// 私有化構造函數,不許外部實例化 /// </summary> private Party() { } /// <summary> /// 找主席 /// </summary> /// <returns></returns> public static Party GetChairman() { if (_chairman == null)//主席不存在則建立一個主席 { _chairman = new Party(); } return _chairman; } public void Say() { Console.WriteLine("同志們好!"); } }
這樣咱們的一個簡單的單單例模式就算完成了,咱們再看看如何調用
class Program { static void Main(string[] args) { Party chairman = Party.GetChairman(); chairman.Say(); } }
那麼問題來了,單線程操做的時候,以上沒有問題,那多線程的時候呢,多我的同時訪問主席,會不會可能產生多個主席呢,多個主席,是否是就違反了單例模式最基本的原則,僅有一個實例呢。多個主席,難道打一架,勝者爲王麼?
因而咱們須要再次優化咱們的代碼
/// <summary> /// 黨派 /// </summary> public class Party { /// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman; /// <summary> /// 靜態只讀的進程輔助對象 /// </summary> private static readonly object syncRoot = new object(); /// <summary> /// 私有化構造函數,不許外部實例化 /// </summary> private Party() { } /// <summary> /// 找主席 /// </summary> /// <returns></returns> public static Party GetChairman() { lock (syncRoot)//鎖你丫的 { if (_chairman == null)//主席不存在則建立一個主席 { _chairman = new Party(); } } return _chairman; } public void Say() { Console.WriteLine("同志們好!"); } } }
以上,再同一個時刻加了鎖的那部分代碼,只有一個線程能夠進入。
若是lock不懂,我知道大家懶
lock 確保當一個線程位於代碼的臨界區時,另外一個線程不進入臨界區。若是其餘線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放
那麼問題又來了,爲何每一次都要鎖,爲何,見一次主席那麼那麼難麼?能不能沒有建立的時候我才鎖,有主席的時候我直接訪問啊
固然能夠,這就是雙重鎖定
/// <summary> /// 黨派 /// </summary> public class Party { /// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman; /// <summary> /// 靜態只讀的進程輔助對象 /// </summary> private static readonly object syncRoot = new object(); /// <summary> /// 私有化構造函數,不許外部實例化 /// </summary> private Party() { } /// <summary> /// 找主席 /// </summary> /// <returns></returns> public static Party GetChairman() { if (_chairman == null) //主席不存在,我再鎖定,進行選舉 { lock (syncRoot) //鎖你丫的 { if (_chairman == null) //主席不存在則建立一個主席 { _chairman = new Party(); } } } return _chairman; } public void Say() { Console.WriteLine("同志們好!"); } } }
這裏咱們就先判斷了主席是否存在,不存在,咱們再閉門選舉,等咱們選舉出來了,你再來訪問。
咱們既優化了執行,也保證了僅有一個主席,保證了多線程訪問的安全性。
其實還有什麼餓漢式,懶漢式的區別
從字面理解
餓漢,一開始我就實例化本身
/// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman = new Party();
懶漢,被引用時候,才實例化本身
/// <summary> /// 我是黨派主席 /// </summary> private static Party _chairman ;
具體使用哪種,應該根據實際狀況來定,無優劣之分,值得注意的是線程安全
總結下
優勢:
一、在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例。
二、避免對資源的多重佔用(好比寫文件操做)。
缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。
使用場景:
一、要求生產惟一序列號。
二、計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
三、建立的一個對象須要消耗的資源過多,好比 I/O 與數據庫的鏈接等。
以上就是關於單例模式的分享
一路前行,風雨無阻