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

