Java的單例設(shè)計(jì)模式詳解
1.什么是單例模式
- 生成一個(gè)獨(dú)一無二的,保證任何時(shí)刻一個(gè)類只有一個(gè)實(shí)例的模式
- 確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)
- 可以在需要時(shí)才創(chuàng)建對(duì)象,避免了全局變量在程序啟動(dòng)時(shí)就得創(chuàng)建對(duì)象的缺點(diǎn)。
2.經(jīng)典單例模式實(shí)現(xiàn)
public class MyInstance{ //第一步:私有化構(gòu)造器,只有類自身才能調(diào)用構(gòu)造器外部類不能夠直接new出這個(gè)類的實(shí)例對(duì)象 private MyInstance(){} //第二步:聲明一個(gè)全局靜態(tài)變量來記錄自身實(shí)例的對(duì)象,也是私有的,限制其它外部類訪問 private static MyInstance myInstance; //第三步:提供一個(gè)外部可訪問的靜態(tài)公開方法,來獲得該類的唯一實(shí)例 public static MyInstance getMyInstance(){ //第四步:進(jìn)行判斷自身類對(duì)象如果為空,則創(chuàng)建一個(gè)實(shí)例 if(myInstance==null){ //這里的方法只執(zhí)行了一次,生成了一個(gè)唯一的類對(duì)象 myInstance=new MyInstance(); } //第五步:如果不為空,則返回該類對(duì)象,故由始至終,該類對(duì)象只初始化過一次,只有一個(gè)對(duì)象存在,這就是單例模式 return myInstance; } }
3.經(jīng)典模式存在的缺陷
這種經(jīng)典模式也稱之為懶漢式單例模式(lazy instantiaze),因?yàn)樗茄舆t化實(shí)例化的,即如果我們不需要這個(gè)實(shí)例,則它永遠(yuǎn)不會(huì)被初始化,只有在調(diào)用過一次實(shí)例化方法后,才會(huì)被創(chuàng)建出對(duì)象。
在多線程的情況下可能會(huì)產(chǎn)生并發(fā)問題,因?yàn)楂@取單例的方法getMyInstance()
有可能被多個(gè)線程同時(shí)訪問,這時(shí)就會(huì)有可能 創(chuàng)建出兩個(gè)以上的實(shí)例對(duì)象。這就要考慮要線程安全的問題了,解決問題就是在獲取實(shí)例方法處加一個(gè)同步鎖,這樣就能輕松地解決線程并發(fā)的問題了。
public static synchronized MyInstance getMyInstance(){ //加同步鎖關(guān)鍵字synchronized,這樣在有線程訪問這個(gè)方法時(shí),其它線程只能等待當(dāng)前線程訪問結(jié)束才能訪問這個(gè)方法。 if(myInstance==null){ //這里的方法只執(zhí)行了一次,生成了一個(gè)唯一的類對(duì)象 myInstance=new MyInstance(); }
4.多線程下同步所造成的性能問題
如果將獲取實(shí)例的方法進(jìn)行同步的話,會(huì)造成程序執(zhí)行的效率大大地下降,而且單例對(duì)象生成只要調(diào)用一次方法即可,之后每次調(diào)用這個(gè)方法時(shí),同步都是一種累綴,有可能會(huì)拖垮程序的性能。
當(dāng)然如果你的程序?qū)τ谛阅艿囊蟛⒉皇呛芨叩脑挘猛降姆椒ǐ@取單例是最簡(jiǎn)單而有效的。
為保證程序的性能并且又不會(huì)出現(xiàn)并發(fā)的問題,可以使用另一種生成單例對(duì)象的模式,叫做餓漢式單例模式(eagerly instantiaze)
public class MyInstance{ //第一步:私有化構(gòu)造器 private MyInstance(){} //第二步:聲明一個(gè)全局靜態(tài)變量來記錄自身實(shí)例的對(duì)象,并進(jìn)行實(shí)例化 private static MyInstance myInstance=new MyInstance(); //第三步:提供一個(gè)外部可訪問的靜態(tài)公開方法,來獲得該類的唯一實(shí)例 public static MyInstance getMyInstance(){ return myInstance; }
這個(gè)模式使JVM在加載這個(gè)類時(shí)會(huì)馬上創(chuàng)建唯一的單例對(duì)象,這樣就能保證任何線程訪問靜態(tài)單例變量myInstance時(shí),單例對(duì)象一定被實(shí)例化過了。
5.利用雙重檢查加鎖來提升性能
- 首先檢查實(shí)例是否已經(jīng)創(chuàng)建了,如果沒有才進(jìn)行同步獲取實(shí)例的方法,這樣就保證了實(shí)例方法只會(huì)在第一次獲取實(shí)例時(shí)會(huì)同步。
- 這里要用到一個(gè)關(guān)鍵字volatile,此關(guān)鍵字確保了當(dāng)實(shí)例變量myInstance被初始化成實(shí)例對(duì)象時(shí),多個(gè)線程能正確地處理實(shí)例變量。注意,這個(gè)關(guān)鍵字只有在Java1.5及以上的版本才會(huì)對(duì)雙重檢查加載生效。
public class MyInstance{ //用關(guān)鍵字volatile修飾實(shí)例變量 private volatile static MyInstance myInstance; //私有化構(gòu)造器 private MyInstance(){} public static MyInstance getMyInstance(){ //第一次檢查實(shí)例是否存在 if(myInstance==null){ //如果不存在則進(jìn)入同步區(qū)塊 synchronized (MyInstance.class){ if(myInstance==null){ //第二次檢查,如果不為空才真正創(chuàng)建實(shí)例對(duì)象 myInstance=new MyInstance(); } } } //如果不為空,則直接返回該類對(duì)象 return myInstance; } }
單例模式的所有情況都已經(jīng)總結(jié)完畢,一開始以為單例模式應(yīng)該是所有設(shè)計(jì)模式中最簡(jiǎn)單易懂的了,沒想到看到四人幫的HeadFirst設(shè)計(jì)模式后發(fā)現(xiàn)還有這么多門道,真的是學(xué)無止境。
注:以上所有內(nèi)容皆總結(jié)自《HeadFirst 設(shè)計(jì)模式》
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
相關(guān)文章
SpringBoot使用@Value實(shí)現(xiàn)給靜態(tài)變量注入值
這篇文章主要介紹了SpringBoot使用@Value實(shí)現(xiàn)給靜態(tài)變量注入值的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07hystrix配置中Apollo與Archaius對(duì)比分析
這篇文章主要為大家介紹了hystrix的配置中Apollo與Archaius對(duì)比分析,并為大家解答在hystrix的配置中有了Apollo是否還需要Archaius這一問題詳解2022-02-02淺談Storm在zookeeper上的目錄結(jié)構(gòu)
這篇文章主要介紹了淺談Storm在zookeeper上的目錄結(jié)構(gòu)的相關(guān)內(nèi)容,涉及storm使用zookeeper的操作以及詳細(xì)結(jié)構(gòu)圖,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10Java中synchronized實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java中synchronized實(shí)現(xiàn)原理詳解,涉及synchronized實(shí)現(xiàn)同步的基礎(chǔ),Java對(duì)象頭,Monitor,Mark Word,鎖優(yōu)化,自旋鎖等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-115分鐘讓你快速掌握java8 stream常用開發(fā)技巧
這篇文章主要給大家介紹了關(guān)于java8 stream常用開發(fā)技巧的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java調(diào)用opencv實(shí)現(xiàn)圖片矯正功能
這篇文章主要為大家詳細(xì)介紹了Java如何調(diào)用opencv實(shí)現(xiàn)圖片矯正功能,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-09-09springboot中mybatis多數(shù)據(jù)源動(dòng)態(tài)切換實(shí)現(xiàn)
在開發(fā)中,動(dòng)態(tài)數(shù)據(jù)源配置還是用的比較多的,比如在多數(shù)據(jù)源使用方面,又或者是在多個(gè)DB之間切換方面。這里給出一個(gè)動(dòng)態(tài)數(shù)據(jù)源的配置方案,感興趣的可以了解一下2021-07-07