小看--单例设计模式
? ? (一)單例設(shè)計(jì)描述
? ? ? 只要了解過設(shè)計(jì)模式的同學(xué)都會(huì)知道:單例設(shè)計(jì)模式,大家都知道單例設(shè)計(jì)模式是一種創(chuàng)建行的設(shè)計(jì)模式。既然是創(chuàng)建型,那么先來講講,對(duì)象的創(chuàng)建的過程吧。
? ? ?--靜態(tài)成員:靜態(tài)成員在程序加載的時(shí)候,就會(huì)加載進(jìn)內(nèi)存。
? ? ?--實(shí)例成員:只有new的時(shí)候才有實(shí)例成員。1、為實(shí)例的數(shù)據(jù)字段分配內(nèi)存,然后初始化對(duì)象的附加字段(類型指針和同步索引塊),最后調(diào)用類型的實(shí)例構(gòu)造器來設(shè)置對(duì)象的初始化狀態(tài)。
? ? ? 單例模式:一般用在一個(gè)類的創(chuàng)建對(duì)象很消耗資源,消耗時(shí)間,并且系統(tǒng)要保證只有一個(gè)對(duì)象的時(shí)候。一句話,對(duì)象的創(chuàng)建并不是很耗時(shí)間,不要刻意去套用單例模式,單例模式必須是在單例的時(shí)候,才單例。
? ? (二)?單例模式的演變
? ? ? ? 下面我們來模擬實(shí)際情況
public class Singleton {/// <summary>/// 對(duì)象會(huì)持有資源/// </summary>private List<string> _connList=new List<string>(){"測(cè)試數(shù)據(jù)庫(kù)連接","測(cè)試數(shù)據(jù)庫(kù)連接2","測(cè)試數(shù)據(jù)庫(kù)連接3","測(cè)試數(shù)據(jù)庫(kù)連接4"};public Singleton(){long lResult = 0;for (int i = 0; i < 100000; i++){lResult += i;}Thread.Sleep(1000);Console.WriteLine("{0}被構(gòu)造一次",this.GetType().Name);}public void Show(){Console.WriteLine("調(diào)用了Show");}}? ? ? 這個(gè)類的創(chuàng)建需要耗費(fèi)很多資源,里面有個(gè)Show方法。
? ? ? 那么接下來,實(shí)際中,我們可能有十個(gè)地方要用到這個(gè)類里面的Show方法,我們的做法是這樣的
//那么接下來,我們這里要調(diào)用十次Show方法for (var i = 0; i < 10; i++) {var singletonObj = new Singleton();singletonObj.Show();}? ????
?
? ? 這里每次調(diào)用一次,都需要耗費(fèi)很多資源和時(shí)間,這里可能有些同學(xué)就會(huì)說,那我把這個(gè)singletonObj=new Singleton()提取出來,放到最外面來。
? ? 那行,按照我們需要,我們把var singletonObj=new Singletone()放到外面,如下所示
? ?
? 貌似這樣就解決了我們的問題,但是各位你們想一想,我們一個(gè)系統(tǒng)是有多個(gè)人開發(fā)的,A這里這樣做,B可能不知道這里有這個(gè)聲明,那他可能就還是一樣去New Singleton,還是導(dǎo)致我們系統(tǒng)中,存在大量這個(gè)對(duì)象。
? 我們應(yīng)該要如何解決這個(gè)問題呢??如何保證這個(gè)對(duì)象在整個(gè)系統(tǒng)只被創(chuàng)建一次呢?
? 單例模式--單線程
? 從上面的問題,我們也可以看出因?yàn)檎l(shuí)都可以在New Singleton,所以導(dǎo)致了這個(gè)問題。那按照這個(gè)想法,那我們就想啦,那就把構(gòu)造函數(shù)私有化唄,私有化完了之后,我們應(yīng)該還要提供一個(gè)方法或者啥的,給外面調(diào)用(也只能是靜態(tài)的成員),構(gòu)造函數(shù)私有化了,外面是不可以New了的
? ?那就按照,剛剛的說法,我們來進(jìn)行一次改進(jìn)
public class Singleton {/// <summary>/// 對(duì)象會(huì)持有資源/// </summary>private List<string> _connList=new List<string>(){"測(cè)試數(shù)據(jù)庫(kù)連接","測(cè)試數(shù)據(jù)庫(kù)連接2","測(cè)試數(shù)據(jù)庫(kù)連接3","測(cè)試數(shù)據(jù)庫(kù)連接4"};private Singleton(){long lResult = 0;for (int i = 0; i < 100000; i++){lResult += i;}Thread.Sleep(1000);Console.WriteLine("{0}被構(gòu)造一次",this.GetType().Name);}public static Singleton CreateInstance(){return new Singleton();}public void Show(){Console.WriteLine("調(diào)用了Show");}}? ? 按照我們上面這個(gè)寫法,把構(gòu)造函數(shù)私有化了,然后在靜態(tài)方法里面New Singletone();
? ? 調(diào)用結(jié)果如下:
for (var i = 0; i < 10; i++) {var singletonObj = Singleton.CreateInstance();singletonObj.Show();} //寫進(jìn)里面去了,是為了模擬有十個(gè)不同的開發(fā),再調(diào)用?
? 那結(jié)果,還是沒有達(dá)到我們想要的,那現(xiàn)在問題就是,對(duì)象沒有重用,因?yàn)槲覀兠看蝞ew,導(dǎo)致了對(duì)象沒有做到重用,那就讓對(duì)象進(jìn)行重用唄。最簡(jiǎn)單的方法,就是給一個(gè)靜態(tài)的字段(為啥靜態(tài)呢,因?yàn)槲覀兡沁叿椒ㄊ庆o態(tài)的),然后做一個(gè)判斷,如果對(duì)象為空,那么我們就創(chuàng)建,如果不為空,就不用創(chuàng)建了,如下所示。? ??
? ?
? ?我們?cè)谠瓉淼幕A(chǔ)上,做了如上圖的改進(jìn)。結(jié)果如下,
? ?
? ?我們想要的結(jié)果實(shí)現(xiàn)了,多次調(diào)用的時(shí)候,做了重用對(duì)象,只構(gòu)造了一次。本來想著單例模式就這樣結(jié)束了(單線程是沒有問題,并且這種實(shí)現(xiàn)是一種懶漢式,懶漢式:當(dāng)你需要用這個(gè)類的時(shí)候,才會(huì)去實(shí)例化)。
? ?編程世界里面,我們總是要考慮一下,多線程的情況。
? ?單例模式--多線程
? ?這個(gè)是用Task.Run()開啟了也給多線程的異步調(diào)用
for (var i = 0; i < 10; i++){Task.Run(()=>{var singletonObj = Singleton.CreateInstance();singletonObj.Show();}); }Thread.Sleep(5000);? ?
? ?通過上面的結(jié)果,我們可以看出,我們的之前的做法,在多線程環(huán)境還是會(huì)調(diào)用多次。
? ?有些同學(xué)就會(huì)說用lock鎖啦,行,我們就給他加上一把鎖。
public class Singleton {/// <summary>/// 對(duì)象會(huì)持有資源/// </summary>private List<string> _connList=new List<string>(){"測(cè)試數(shù)據(jù)庫(kù)連接","測(cè)試數(shù)據(jù)庫(kù)連接2","測(cè)試數(shù)據(jù)庫(kù)連接3","測(cè)試數(shù)據(jù)庫(kù)連接4"};private static Singleton singletonObj = null;private static readonly object singleTonObjLock = new object(); //加鎖,之后這里為啥要用readonly,大家可以找private Singleton(){long lResult = 0;for (int i = 0; i < 100000; i++){lResult += i;}Thread.Sleep(1000);Console.WriteLine("{0}被構(gòu)造一次",this.GetType().Name);Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}調(diào)用一次");}public static Singleton CreateInstance(){lock (singleTonObjLock) //很多同學(xué)都說用lock this,this肯定是不行的,因?yàn)閘ock是lock引用的,如果這個(gè)this的引用改變了... {if (singletonObj == null){singletonObj = new Singleton();}}return singletonObj;}public void Show(){Console.WriteLine("調(diào)用了Show");}}? ?看我們給他加完鎖的時(shí)候效果。
? ?
? ?嗯,實(shí)現(xiàn)了我們想要的效果了,說明我們加鎖是有效果的。到了這個(gè)時(shí)候,大家可能覺得一個(gè)單例模式應(yīng)該就快結(jié)束了,那么我們?cè)賮砜纯催@種情況。
? 單例模式--多線程(雙if+lock)
??
?
?通過上面的介紹,我們理解了單例模式的演變過程,也對(duì)單例模式,多線程有了更加深刻的印象。
?(三)單例模式其他實(shí)現(xiàn)
? ? 就像我們一開始說的那樣,單例模式,其實(shí)一個(gè)進(jìn)程內(nèi),在多線程環(huán)境下,如何保證只有一個(gè)對(duì)象,這就是單例。也可以從這個(gè)定義看出,我們可以通過靜態(tài)的構(gòu)造函數(shù)來實(shí)現(xiàn)一個(gè)單例模式。
? ? 靜態(tài)構(gòu)造函數(shù),是由CLR保證的,有且只會(huì)加載一次。
? ? 其他很多方法實(shí)現(xiàn),都是利用static關(guān)鍵字的背后原因,在第一次使用類型之前被調(diào)用,且只會(huì)被調(diào)用一次。
? ?(四)懶漢式,餓漢式
? ? ? 懶漢,就是說這個(gè)人很懶,需要用的時(shí)候,才構(gòu)建。雙if+lock這種就屬于懶漢式。懶漢式,利用了延遲加載加載的思想。
? ? ? 餓漢:就是調(diào)用我這個(gè)類型,就會(huì)幫你創(chuàng)建好;管你用不用,我都會(huì)幫你創(chuàng)建;就是餓了嗎,我后面介紹的利用static關(guān)鍵字的就是屬于餓漢式;餓漢式:管你之前有沒有(內(nèi)存中),都會(huì)幫你new一個(gè)新的實(shí)例。
? ?(五)單例模式的使用場(chǎng)景
? ? ? ?單例:必須單例才單例,反正沒必要。單例模式實(shí)現(xiàn)都有性能,損失,靜態(tài)方法。
? ? ? ?單例:會(huì)把對(duì)象常駐內(nèi)存,靜態(tài)的。
? ? ? ?單例的使用,多個(gè)人操作可能會(huì)對(duì)你影響,因?yàn)槎际菍?duì)同一份引用進(jìn)行修改。
? ? ? ?一般用在數(shù)據(jù)庫(kù)連接,打印機(jī),遠(yuǎn)程服務(wù)調(diào)用,等等這些大對(duì)象身上。
? //單例模式的應(yīng)用場(chǎng)景補(bǔ)充:讀取配置文件信息,讀取配置文件不應(yīng)該一直new一個(gè)類,應(yīng)該是單例的。
? ? ? ?
?
? ? ??
?
public class ConfigV1{private static object _lockObj = new object();private static ConfigV1 _instance;private static Dictionary<string,string> _configItems =new Dictionary<string, string>();private ConfigV1(){Init();Console.WriteLine($"{this.GetType().Name}被構(gòu)造一次");}public static ConfigV1 GetInstance(){if (_instance == null){lock (_lockObj){if (_instance == null){_instance=new ConfigV1();}}}return _instance;}public string this[string item]{get{return _configItems[item];}}private void Init(){var config=ConfigurationManager.AppSettings.AllKeys;foreach(var key in config){_configItems.Add(key,ConfigurationManager.AppSettings[key]);}}}? ? ? ?github:https://github.com/gdoujkzz/DesignPattern.git
謝謝你閱讀我的博客,如果有收獲,請(qǐng)點(diǎn)一個(gè)贊(推薦)
? ? ?
?
? ?
?
?
?
? ? ?
轉(zhuǎn)載于:https://www.cnblogs.com/gdouzz/p/8319485.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的小看--单例设计模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链家app怎么看成交记录(链家这些房产A
- 下一篇: Jmeter(二)Jmeter目录介绍