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