常见设计模式—单例模式(Singleton)
前言
好久沒寫東西了,但是想著無論什么事還是要堅(jiān)持自己初心要堅(jiān)持的東西。寫東西不能斷!
對于常用的23種設(shè)計(jì)模式,這里筆者會根據(jù)自己學(xué)習(xí)和出現(xiàn)頻率、重要程度進(jìn)行學(xué)習(xí)記錄吧。并且每種設(shè)計(jì)模式可能會根據(jù)暫時需求側(cè)重學(xué)習(xí)深淺。
單例模式
有很多直接把單例分成很多種,這里我就分兩個大類(餓漢懶漢)在這里說啦。
單例模式(Singleton Pattern)是設(shè)計(jì)模式中最簡單的模式之一,屬于創(chuàng)建型模式。這種設(shè)計(jì)模式主要是類的對象只有一個實(shí)例,不需要每次new 創(chuàng)造。而我們要做的的就是確保這個對象創(chuàng)建的唯一。然后根據(jù)一些特征進(jìn)行優(yōu)化創(chuàng)建以及訪問改類。
而單例模式也有很多的應(yīng)用,比如很多驅(qū)動例如攝像頭、打印機(jī)等等,而在javaweb中的spring有很多配置文件,掌控全局,同樣也是單例的。
對于單例,主要是全局只有這么一個對象。對了它的理解,這里筆者打幾個有可能不太恰當(dāng)?shù)睦斫?#xff0c;目的在于幫助理解記憶,如果有錯誤還請指正。
個人可能不太恰當(dāng)?shù)睦斫?/strong>:
- 一定程度上單例模式和普通的模式有可能是一根根火柴和打火機(jī)的差別。火柴想燃燒每次都要一根火柴摩擦燃燒為灰燼作為代價。而一個打火機(jī)可以持續(xù)供給。同樣如果你只想在某個線程某個類想要得到執(zhí)行改類的一個方法,如果這個類占用的內(nèi)存、空間巨大,耗費(fèi)的時間也很大的話,如果頻繁創(chuàng)造是一筆很大的負(fù)擔(dān),那樣就不如咱們就創(chuàng)建好一個然后供我們使用就好啦。
- 一定程度又像是他人和個人的區(qū)別。每個人都可能干過相同的事情,可能很多人都在校園的跑道上跑過步,但他們并不是你,你也可能在操場上跑過很多圈。但只有你知道你的思考。同樣單例模式在一定程度可以看作一個人生,它可能有很多職能,也可能干過很多事、重復(fù)過很多事。但它可能對自己有從始至終的思考和記錄。它可能有些全局參數(shù)記錄著程序從始至終一些狀態(tài)、信息等等。也就是它一直是它,你也一直是你獨(dú)特的自己。
- 其他等等
至于單例模式的優(yōu)缺點(diǎn),這里就不作詳細(xì)介紹了。無非是關(guān)于性能職能、時間、空間、拓展性等方面的一些討論。這個可以參考不同人的不同理解。本文主要記錄單例模式的實(shí)現(xiàn)方面。而同樣單例模式實(shí)現(xiàn)上分為餓漢式和懶漢式:
單例模式創(chuàng)建要求:
- 某個類只能有一個實(shí)例(構(gòu)造器私有化)
- 它必須自行創(chuàng)建這個實(shí)例(含有一個改類的靜態(tài)變量來保存這個唯一的實(shí)例)
- 自行向整個系統(tǒng)提供這個實(shí)例(直接暴露或者用靜態(tài)變量的get方法)
餓漢式
直接創(chuàng)建對象,不存在線程安全問題 。對于餓漢式的實(shí)現(xiàn)是相對簡單和容易的。在實(shí)際遇到這種類似的思想其實(shí)也很多。
理解:
- 這個餓漢式就好比雙十一,你一下把你家里感覺可能缺的日后可能需要(也不一定需要的)全部給買了。比如啥洗衣液屯、棉衣棉鞋棉被日后可能要買也買。然后啥微波爐、烤箱我感覺可能以后會用我也買買買。買買買。這一下就買全了。但是:
- 同樣在實(shí)際的生產(chǎn),我們一個項(xiàng)目中可能有很多這樣單例的存在,如果一次性啟動的話對時間花銷真的是太大。可能對程序的壓力和體驗(yàn)都很差。所以一般很少直接使用。這種體驗(yàn),像極了你打開某個網(wǎng)頁等待JavaScript和css的過程。
餓漢式的幾種實(shí)現(xiàn)方式:
01 . 直接實(shí)例化餓漢式(簡潔直觀)
02 . 枚舉式(最簡潔)
public enum singleton2 {INSTANCE }03 . 靜態(tài)代碼塊餓漢式(適合復(fù)雜實(shí)例化)
public class singleton3 {public static final singleton3 INSTANCE;static {/**** 有可能一些數(shù)據(jù)需要配置,需要從類加載器加載* 例如從某xxx.properties加載某些配置信息,我們只需更改配置文件不需要更改代碼*/INSTANCE=new singleton3();}private singleton3(){} }懶漢式
我們知道餓漢式在早早把對象創(chuàng)建好,在執(zhí)行一些其他邏輯時候不存在線程安全的問題。但是懶漢式就不一樣啦,延遲創(chuàng)建對象可能會有線程安全問題。
01 .線程不安全(適合單線程)
對于這種單線程沒問題,但是如果如果兩個或多個線程來同時訪問instance如果都為null同時申請的時候會遇到下圖等之類問題,這違背了單例模式的設(shè)計(jì)原則并且可能會對系統(tǒng)數(shù)據(jù)造成錯誤。
02 .線程安全(適用于多線程)
怎么優(yōu)化上述的懶漢式單例模式呢?既然不允許多個線程這樣同時訪問,那么咱們給它上個鎖不久行了嘛!
但是這樣有啥問題呢?就是我獲取這個單例對象的時候,被上鎖了。你氣不氣?我們就是在創(chuàng)建這個單例時候多線程可能出現(xiàn)點(diǎn)問題,咱么獲取的時候不存在線程安全問題啊。。你為啥獲取也要鎖??
這就好比,咱們都喜歡看美女,跟美女說話要排隊(duì)一個一個來,只能一個在一個時刻,但是你看美女不需要排隊(duì)啊!!!
03 .線程安全推薦版(適用于多線程)
對于上述存在的問題,咱們只需要雙重判定就行了。
- 首先判斷instance是不是為null。如果不是null說明被實(shí)例化過直接返回即可!nice!
- 如果instance為null?上鎖之后再判空實(shí)例化。
兩個判斷為null?為啥要兩個?
- 第一個:用來判斷是否為空需要上鎖new 實(shí)例。
- 第二個:上鎖后雖然只有一個會執(zhí)行當(dāng)前方法,但是很可能都為null的時候兩個或多個都為null上鎖的想構(gòu)造。然后后面線程在等待同時前面線程構(gòu)造好了,那么它就不需要再去構(gòu)造這個instance啦!直接不操作就行了。
就這樣,稍微完美的方法就這樣產(chǎn)生了。
04 .靜態(tài)內(nèi)部類形式(適用于多線程)
上面的方法可能有些復(fù)雜,而靜態(tài)內(nèi)部類也是個好方式。主要是靜態(tài)內(nèi)部類和外部類不是一起加載的,并且你去調(diào)用它的時候他就會初始化,并且類加載是線程安全的,這個不需要考慮線程安全問題。當(dāng)加載完之后你就可以直接拿啦。這樣也能達(dá)到延遲加載的作用。
這個更詳細(xì)你可以自己研究靜態(tài)變量(類變量)、靜態(tài)內(nèi)部類等等加載順序,研究下`static關(guān)鍵字。
public class singleton7 {private singleton7(){}private static class inner{private static final singleton7 instance=new singleton7();}public static singleton7 getInstance(){return inner.instance;} }結(jié)語
學(xué)習(xí)參考尚學(xué)堂單例講解以及百科、菜鳥教程等等,有些區(qū)別但是大部分實(shí)現(xiàn)都是相似的,帶上個人理解分享給大家。如果有問題和疏漏還請指教!
歡迎交流學(xué)習(xí)、歡迎關(guān)注微信公眾號:bigsai
總結(jié)
以上是生活随笔為你收集整理的常见设计模式—单例模式(Singleton)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈迷宫搜索类的双向bfs问题(例题解析
- 下一篇: 玩转服务器(华为云)—购买配置登录篇