您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java的單例模式如何實(shí)現(xiàn)”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“Java的單例模式如何實(shí)現(xiàn)”文章能幫助大家解決問題。
單例模式(Singleton Pattern)是一個(gè)比較簡單的模式,實(shí)際應(yīng)用很廣泛,比如 Spring 中的Bean實(shí)例就是一個(gè)單例對象。
定義:確保某一個(gè)類 只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
優(yōu)點(diǎn)
只有一個(gè)實(shí)例,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實(shí)例。
單例模式可以避免對資源的多重占用,例如一個(gè)寫文件動(dòng)作,由于只有一個(gè)實(shí)例存在內(nèi)存中,避免對同一個(gè)資源文件的同時(shí)寫操作。
單例模式可以在系統(tǒng)設(shè)置全局的訪問點(diǎn),優(yōu)化和共享資源訪問,例如可以設(shè)計(jì)一個(gè)單例類,負(fù)責(zé)所有數(shù)據(jù)表的映射處理。
缺點(diǎn)
單例模式一般沒有接口,很難擴(kuò)展(根據(jù)環(huán)境而定)。
單例模式與單一職責(zé)原則有沖突。一個(gè)類應(yīng)該只實(shí)現(xiàn)一個(gè)邏輯,而不關(guān)心是否是單例的。
單例模式有很多的實(shí)現(xiàn)方式,但是各種實(shí)現(xiàn)的方式都有其優(yōu)缺點(diǎn),下面來看看各種的實(shí)現(xiàn)方式。
單例模式的實(shí)現(xiàn)滿足以下幾點(diǎn):構(gòu)造方法私有。有一個(gè)靜態(tài)方法獲取該類的實(shí)例。該類保證只有一個(gè)實(shí)例。
懶漢式是當(dāng)用到這個(gè)對象的時(shí)候才會(huì)創(chuàng)建。
懶漢式,在需要單例模式類實(shí)例時(shí)它才創(chuàng)建出來給你(因?yàn)楹軕校?/p>
優(yōu)點(diǎn):只有用到的時(shí)候才會(huì)創(chuàng)建這個(gè)對象,因此節(jié)省資源。
簡單的實(shí)現(xiàn)如下:
/** *Singleton類,單例模式類,在類加載時(shí)便會(huì)創(chuàng)建一個(gè)私有靜態(tài)變量instance,也就是該類的實(shí) *例,再通過公共接口getInstance()來發(fā)布該實(shí)例 */ public class Singleton { private static Singleton instance; //私有化構(gòu)造方法防止外界new對象 private Singleton (){
}
//公有化靜態(tài)函數(shù),對外暴露單例對象的接口
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
但是這種方式并不能保證這是唯一的單例,在高并發(fā)訪問下,多個(gè)線程同時(shí)訪問到這個(gè)單例時(shí),還是有可能不能保證這個(gè)類就是單例的
為了保證線程安全,我們可以加鎖,給這個(gè)getInstance()方法加上線程同步鎖synchronize具體實(shí)現(xiàn)如下:
public class Singleton { private static Singleton instance; private Singleton (){ } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
但是這種方式一旦加鎖,雖然可以保證其實(shí)單例且線程安全的,但是在高并發(fā)訪問下性能必然是受到影響,多個(gè)線程都需要用到該單例時(shí),就無法保證速度,需要同步地等待這個(gè)單例使用完回到JVM中的堆區(qū)(Heap)才可以繼續(xù)使用這個(gè)單例,效率十分的低。
還有一種是雙重檢查式,兩次判斷
Double Check Lock(DCL)方式實(shí)現(xiàn)單例模式
public class Singleton{ private volatile static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if(instance == null){ synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; }}
我們可以看到getInstance中對instance進(jìn)行了兩次判斷是否為空,第一次判斷主要是為了避免不必要的同步問題,第二次判斷則是為了在null的情況下創(chuàng)建實(shí)例,因?yàn)樵谀承┣闆r下會(huì)出現(xiàn)失效問題即DCL失效問題,可以使用volatile關(guān)鍵字處理這個(gè)問題,但是同樣的,使用了volatile關(guān)鍵字也會(huì)對性能有一定的影響。但是優(yōu)點(diǎn)在于資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)對象才被實(shí)例化,但是DCL也因?yàn)榈谝淮渭虞d時(shí)反應(yīng)慢,所以在高并發(fā)情況下也會(huì)有一定的缺陷。
餓漢式和懶漢式恰巧相反,在類加載的時(shí)候就創(chuàng)建實(shí)例。
單例模式類迫不及待的想要?jiǎng)?chuàng)建實(shí)例了(因?yàn)轲I了)
優(yōu)點(diǎn):還沒用到就創(chuàng)建,浪費(fèi)資源。
缺點(diǎn):在類加載的時(shí)候就創(chuàng)建,線程安全。
實(shí)現(xiàn)如下:
/** *這種方式在類加載時(shí)就完成了初始化,所以類加載較慢,但是獲取對象的速度快。這種方式 *基于類加載機(jī)制,避免了多線程的同步問題。如果從來沒有使用過這個(gè)實(shí)例,則會(huì)造成內(nèi)存 *的浪費(fèi)。 */public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){ } public static Singleton getInstance() { return instance; } }
匿名內(nèi)部類/靜態(tài)內(nèi)部類
利用靜態(tài)變量、靜態(tài)代碼塊、靜態(tài)方法都是在類加載的時(shí)候只加載一次的原理。
實(shí)現(xiàn)如下
public class Singleton { private static Singleton instance; //靜態(tài)塊在類加載時(shí)會(huì)被執(zhí)行,也就創(chuàng)建了Singleton類實(shí)例 static{ instance = new Singleton(); } private Singleton (){ } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
/** *Java靜態(tài)內(nèi)部類的特性是,加載的時(shí)候不會(huì)加載內(nèi)部靜態(tài)類,使用的時(shí)候才會(huì)進(jìn)行加載。 *第一次加載Singleton類時(shí)并不會(huì)初始化sInstance,只有第一次調(diào)用getInstance方法時(shí)虛 *擬機(jī)加載SingletonHolder并初始化sInstance。這樣不僅能確保線程安全,也能保證 *Singleton類的唯一性。所以,推薦使用靜態(tài)內(nèi)部類單例模式 */public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){ } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
/** *默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例。 *枚舉單例的有點(diǎn)就是簡單,缺點(diǎn)是可讀性不高。 */ public enum Singleton { //外部調(diào)用由原來的Singleton.getInstance變成了Singleton.INSTANCE了。 INSTANCE; }
單例模式是運(yùn)用頻率很高的模式,在我們客戶端通常是沒有高并發(fā)的情況,所以選擇哪種方式并不會(huì)有太大的影響。出于效率考慮,推薦使用靜態(tài)內(nèi)部類的單例模式和DCL的單例模式。
優(yōu)點(diǎn):
由于單例模式在內(nèi)存中只有一個(gè)實(shí)例,減少內(nèi)存開支,特別是一個(gè)對象需要頻繁創(chuàng)建、銷毀時(shí),而且創(chuàng)建或銷毀時(shí)性能又無法優(yōu)化,單例模式的優(yōu)勢就十分明顯。
由于單例模式只生成一個(gè)實(shí)例,所以減少了系統(tǒng)的性能開銷,當(dāng)一個(gè)對象的產(chǎn)生需要比較多的資源時(shí),如讀取配置、產(chǎn)生其他依賴對象時(shí),則可通過在應(yīng)用啟動(dòng)時(shí)直接產(chǎn)生一個(gè)單例對象,然后用永久駐留的方式解決。
單例模式可以避免對資源的多重占用,如一個(gè)文件的操作,由于只有一個(gè)實(shí)例存在內(nèi)存中,避免對同一個(gè)資源文件的同時(shí)操作。
單例模式可以在系統(tǒng)設(shè)置全局的訪問點(diǎn),優(yōu)化和共享資源訪問。例如,可以設(shè)計(jì)一個(gè)單例類,負(fù)責(zé)所有數(shù)據(jù)表的映射處理。
缺點(diǎn):
單例模式一般沒有接口,擴(kuò)展很困難,除非修改代碼。
單例對象如果持有Context,那么很容易引發(fā)內(nèi)存泄露,此時(shí)需要注意傳遞給單例對象的Context最好是Application Context。
不適合用于變化頻繁的對象;如果實(shí)例化的對象長時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為該對象是垃圾而被回收,這可能會(huì)導(dǎo)致對象狀態(tài)的丟失;
網(wǎng)站訪問量計(jì)數(shù)器。
項(xiàng)目中用于讀取配置文件的類。
Spring中,每個(gè)Bean默認(rèn)都是單例的,這樣便于Spring容器進(jìn)行管理。
關(guān)于“Java的單例模式如何實(shí)現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。