更新時(shí)間:2020年08月24日14時(shí)02分 來源:傳智播客 瀏覽次數(shù):
單例模式(Singleton Pattern)顧名思義就是只有一個(gè)實(shí)例,是一種常用的軟件設(shè)計(jì)模,設(shè)計(jì)模式屬于創(chuàng)建型模式它提供了一種創(chuàng)建對象的最佳方式,但是在Java中要用好單例模式,并不是一件簡單的事。在整個(gè)系統(tǒng)中,單例類只能有一個(gè)實(shí)例對象,且需要自行完成示例,并始終對外提供同一實(shí)例對象。
注意:
1、單例類只能有一個(gè)實(shí)例。
2、單例類必須自己創(chuàng)建自己的唯一實(shí)例。
3、單例類必須給所有其他對象提供這一實(shí)例。
意圖:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。
主要解決:一個(gè)全局使用的類頻繁地創(chuàng)建與銷毀。
何時(shí)使用:當(dāng)您想控制實(shí)例數(shù)目,節(jié)省系統(tǒng)資源的時(shí)候。
如何解決:判斷系統(tǒng)是否已經(jīng)有這個(gè)單例,如果有則返回,如果沒有則創(chuàng)建。
關(guān)鍵代碼:構(gòu)造函數(shù)是私有的。
優(yōu)點(diǎn):
在內(nèi)存中只有一個(gè)對象,節(jié)省內(nèi)存空間;
避免頻繁的創(chuàng)建銷毀對象,可以提高性能;
避免對共享資源的多重占用,簡化訪問;
為整個(gè)系統(tǒng)提供一個(gè)全局訪問點(diǎn)。
缺點(diǎn):
不適用于變化頻繁的對象;
濫用單例將帶來一些負(fù)面問題,如為了節(jié)省資源將數(shù)據(jù)庫連接池對象設(shè)計(jì)為的單例類,可能會(huì)導(dǎo)致共享連接池對象的程序過多而出現(xiàn)連接池溢出;
如果實(shí)例化的對象長時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為該對象是垃圾而被回收,這可能會(huì)導(dǎo)致對象狀態(tài)的丟失;
如果我們在代碼中需要一個(gè)全局類,我們可以讓它變成一個(gè)單例。例如,我們在系統(tǒng)的多個(gè)地方需要讀取一個(gè)配置文件,我們并不需要每次都去new一個(gè)實(shí)例,然后去讀文件,只需要維護(hù)一個(gè)全局的Config類,并且每次使用的時(shí)候校驗(yàn)下文件是否變更即可。依賴可以減少類的創(chuàng)建跟銷毀的時(shí)候的開銷,二來也減少了讀取文件的次數(shù)。又如我們需要維護(hù)一個(gè)計(jì)數(shù)器,我們當(dāng)然不想每次統(tǒng)計(jì)的時(shí)候都穿透去寫DB,我們可以先寫到內(nèi)存當(dāng)中。還有,在程序開發(fā)中,我們常常運(yùn)用到各種池化技術(shù),我們可以將線程池,連接池作為一個(gè)單例,統(tǒng)一進(jìn)行分配跟管理
單例模式在多線程的應(yīng)用場合下必須小心使用。如果當(dāng)唯一實(shí)例尚未創(chuàng)建時(shí),有兩個(gè)線程同時(shí)調(diào)用創(chuàng)建方法,那么它們同時(shí)沒有檢測到唯一實(shí)例的存在,從而同時(shí)各自創(chuàng)建了一個(gè)實(shí)例,這樣就有兩個(gè)實(shí)例被構(gòu)造出來,從而違反了單例模式中實(shí)例唯一的原則。解決這個(gè)問題的辦法是為指示類是否已經(jīng)實(shí)例化的變量提供一個(gè)互斥鎖(雖然這樣會(huì)降低效率)。
單例模式有很多種寫法,但是有些寫法在特定的場景下,尤其是多線程條件下,無法滿足實(shí)現(xiàn)單一實(shí)例對象的要求,從而導(dǎo)致錯(cuò)誤。下面我們來介紹單例模式的五種寫法。
1、懶漢式
懶漢式,顧名思義就是實(shí)例在用到的時(shí)候才去創(chuàng)建,“比較懶”,用的時(shí)候才去檢查有沒有實(shí)例,如果有則返回,沒有則新建。有線程安全和線程不安全兩種寫法,區(qū)別就是synchronized關(guān)鍵字。
優(yōu)點(diǎn):獲取對象的速度快,線程安全(因?yàn)樘摂M機(jī)保證只會(huì)裝載一次,在裝載類的時(shí)候是不會(huì)發(fā)生并發(fā)的)
缺點(diǎn):耗內(nèi)存(若類中有靜態(tài)方法,在調(diào)用靜態(tài)方法的時(shí)候類就會(huì)被加載,類加載的時(shí)候就完成了單例的初始化,拖慢速度)
2、餓漢式
餓漢式,從名字上也很好理解,就是“比較勤”,實(shí)例在初始化的時(shí)候就已經(jīng)建好了,不管你有沒有用到,都先建好了再說。
優(yōu)點(diǎn):單例只有在使用時(shí)才被實(shí)例化,一定程度上節(jié)約了資源
缺點(diǎn):加入synchronized關(guān)鍵字,造成不必要的同步開銷。不建議使用。
3、雙檢鎖
雙檢鎖,又叫雙重校驗(yàn)鎖,綜合了懶漢式和餓漢式兩者的優(yōu)缺點(diǎn)整合而成??瓷厦娲a實(shí)現(xiàn)中,特點(diǎn)是在synchronized關(guān)鍵字內(nèi)外都加了一層 if 條件判斷,這樣既保證了線程安全,又比直接上鎖提高了執(zhí)行效率,還節(jié)省了內(nèi)存空間。
優(yōu)點(diǎn):線程安全;延遲加載;效率較高。
4、靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類的方式效果類似雙檢鎖,但實(shí)現(xiàn)更簡單。但這種方式只適用于靜態(tài)域的情況,雙檢鎖方式可在實(shí)例域需要延遲初始化時(shí)使用。
優(yōu)點(diǎn):避免了線程不安全,延遲加載,效率高。
5、枚舉
枚舉的方式是比較少見的一種實(shí)現(xiàn)方式,但是看上面的代碼實(shí)現(xiàn),卻更簡潔清晰。并且她還自動(dòng)支持序列化機(jī)制,絕對防止多次實(shí)例化。
優(yōu)點(diǎn)
系統(tǒng)內(nèi)存中該類只存在一個(gè)對象,節(jié)省了系統(tǒng)資源,對于一些需要頻繁創(chuàng)建銷毀的對象,使用單例模式可以提高系統(tǒng)性能。
缺點(diǎn)
當(dāng)想實(shí)例化一個(gè)單例類的時(shí)候,必須要記住使用相應(yīng)的獲取對象的方法,而不是使用new,可能會(huì)給其他開發(fā)人員造成困擾,特別是看不到源碼的時(shí)候。
Java設(shè)計(jì)模式之單例模式以及單例所引發(fā)的思考
Java培訓(xùn)實(shí)戰(zhàn)教程之單例模式
北京校區(qū)