摘要:前言文章介紹了單例模式五種實(shí)現(xiàn)的方式,分別是懶漢,餓漢,靜態(tài)內(nèi)部類(lèi),雙重檢驗(yàn)鎖以及枚舉實(shí)現(xiàn)方式,并主要關(guān)心加載時(shí)機(jī)以及線(xiàn)程安全。
前言
文章介紹了單例模式五種實(shí)現(xiàn)的方式,分別是懶漢,餓漢,靜態(tài)內(nèi)部類(lèi),雙重檢驗(yàn)鎖以及枚舉實(shí)現(xiàn)方式,并主要關(guān)心加載時(shí)機(jī)以及線(xiàn)程安全。首先,通俗點(diǎn)講,餓漢就是這個(gè)類(lèi)還沒(méi)被使用到的時(shí)候,實(shí)例已經(jīng)創(chuàng)建好了;而懶漢是使用到的時(shí)候才創(chuàng)建對(duì)應(yīng)的實(shí)例。線(xiàn)程安全方面主要考慮實(shí)例化時(shí)候是否確保一個(gè)實(shí)例,對(duì)于單例類(lèi)中其他方法的線(xiàn)程安全不予考慮。
懶漢模式先來(lái)一個(gè)最直觀(guān)的代碼:
public class Singleton { private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ //如果還沒(méi)有被實(shí)例化過(guò),就實(shí)例化一個(gè),然后返回 if(instance == null){ instance = new Singleton(); } return instance; } }
這段代碼里,我們沒(méi)有考慮線(xiàn)程安全,所以可能就會(huì)產(chǎn)生多個(gè)實(shí)例,但是這個(gè)例子能很好的表達(dá)單例模式的思想,就是保持只有一個(gè)實(shí)例,先理解了基礎(chǔ),我們?cè)僖徊讲桨l(fā)展。
所以線(xiàn)程安全的事情還是得解決啊~于是有了以下的代碼:
public class Singleton { private static Singleton instance = null; private Singleton(){} public static synchronized Singleton getInstance(){ //如果還沒(méi)有被實(shí)例化過(guò),就實(shí)例化一個(gè),然后返回 if(instance == null){ instance = new Singleton(); } return instance; } }
這段代碼只加了一個(gè)關(guān)鍵字synchronized用于確保getInstance方法線(xiàn)程安全,但是這種方式問(wèn)題很大啊,畢竟所有的線(xiàn)程到了這個(gè)方法全得排隊(duì)等著,對(duì)性能的損耗非常大,不過(guò)沒(méi)關(guān)系,我們這里著重先解決掉線(xiàn)程安全的問(wèn)題,接下來(lái)會(huì)有辦法解決這個(gè)效率低下的問(wèn)題(如果你著急那就直接去看雙重校驗(yàn)鎖吧...)
懶漢模式將實(shí)例化的時(shí)機(jī)放到了需要使用的時(shí)候(餓漢是類(lèi)加載了就有實(shí)例),也就是“延遲加載”,相比餓漢,能避免了在加載的時(shí)候?qū)嵗锌赡苡貌坏降膶?shí)例,但是問(wèn)題也很明顯,我們要花精力去解決線(xiàn)程安全的問(wèn)題。
餓漢模式餓漢模式相比懶漢模式,在類(lèi)加載的時(shí)候就已經(jīng)存在一個(gè)實(shí)例,舉個(gè)例子,比如數(shù)據(jù)庫(kù)連接吧,懶漢就是第一次訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的時(shí)候我才去創(chuàng)建一個(gè)連接,而餓漢呢,是你程序啟動(dòng)了,類(lèi)加載好了的時(shí)候,我已經(jīng)有個(gè)連接了,你用不用不一定了,所以餓漢的缺點(diǎn)也就出來(lái)了:可能會(huì)產(chǎn)生很多無(wú)用的實(shí)例。
public class Singleton { //類(lèi)加載的時(shí)候instance就已經(jīng)指向了一個(gè)實(shí)例 private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
那么加載時(shí)機(jī)的問(wèn)題我們已經(jīng)說(shuō)過(guò)了,接下來(lái)就是線(xiàn)程安全了,代碼里我們并沒(méi)有看見(jiàn)synchronized關(guān)鍵字,那么這種方式是如何確保線(xiàn)程安全的呢,這個(gè)就是JVM類(lèi)加載的特性了,JVM在加載類(lèi)的時(shí)候,是單線(xiàn)程的,所以可以保證只存在單一的實(shí)例。
雙重校驗(yàn)鎖首先要說(shuō)明的是,雙重檢驗(yàn)鎖也是一種延遲加載,并且較好的解決了在確保線(xiàn)程安全的時(shí)候效率低下的問(wèn)題。
public class Singleton { private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
對(duì)比一下最原始的那種線(xiàn)程安全的方法(就是懶漢模式的第二種代碼),那種方法將整個(gè)getInstance方法鎖住,那么每次調(diào)用那個(gè)方法都要獲得鎖,釋放鎖,等待等等...而雙重校驗(yàn)鎖鎖住了部分的代碼。進(jìn)入方法如果檢查為空才進(jìn)入同步代碼塊,這樣很明顯效率高了很多。
有人疑問(wèn)為什么instance==null要判斷兩次嗎,那我們先去掉第二次的判斷。
如果兩個(gè)線(xiàn)程一起調(diào)用getInstance方法,并且都通過(guò)了第一次的判斷instance==null,那么第一個(gè)線(xiàn)程獲取了鎖,然后實(shí)例化了instance,然后釋放了鎖,然后第二個(gè)線(xiàn)程得到了線(xiàn)程,然后馬上也實(shí)例化了instance,這就尷尬了。單例模式就失敗了。
所以加上第二次判斷后,先進(jìn)來(lái)的線(xiàn)程判斷了一下,哦,為空,我創(chuàng)建一個(gè),然后創(chuàng)建一個(gè)實(shí)例之后釋放了鎖,第二個(gè)線(xiàn)程進(jìn)來(lái)之后,哎?已經(jīng)有了,那我就不用創(chuàng)建了,然后釋放了鎖,開(kāi)開(kāi)心心的完成了單例模式。
懶漢模式需要考慮線(xiàn)程安全,所以我們多寫(xiě)了好多的代碼,餓漢模式利用了類(lèi)加載的特性為我們省去了線(xiàn)程安全的考慮,那么,既能享受類(lèi)加載確保線(xiàn)程安全帶來(lái)的便利,又能延遲加載的方式,就是靜態(tài)內(nèi)部類(lèi)。Java靜態(tài)內(nèi)部類(lèi)的特性是,加載的時(shí)候不會(huì)加載內(nèi)部靜態(tài)類(lèi),使用的時(shí)候才會(huì)進(jìn)行加載。而使用到的時(shí)候類(lèi)加載又是線(xiàn)程安全的,這就完美的達(dá)到了我們的預(yù)期效果~
public class Singleton { private static class SingletonHolder{ private static Singleton instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } }枚舉
JDK1.5提供了一個(gè)新的數(shù)據(jù)類(lèi)型,枚舉。枚舉的出現(xiàn)提供了一個(gè)較為優(yōu)雅的方式取代以前大量的static final類(lèi)型的變量。而這里,我們也利用枚舉的特性,實(shí)現(xiàn)了單例模式,這種思路是《Effective Java》中第三條最后一段給出的實(shí)現(xiàn)方式,有興趣的可以看看這本書(shū)~
代碼簡(jiǎn)單到無(wú)法理解:
public enum Singleton { INSTANCE; }
外部調(diào)用由原來(lái)的Singleton.getInstance變成了Singleton.INSTANCE了。
這里要注意,原來(lái)的class已經(jīng)換成了關(guān)鍵字enum,但是其實(shí)無(wú)所謂的,看下繼承關(guān)系就能知道,其實(shí)還是一個(gè)class
而且我們可以查看Enum的源碼:
這里能看到實(shí)現(xiàn)了Serializable接口,所以不用考慮序列化的問(wèn)題(其實(shí)序列化反序列化也能導(dǎo)致單例失敗的,但是我們這里不過(guò)多研究)。對(duì)于線(xiàn)程安全,同樣的,加載的時(shí)候JVM能確保只加載一個(gè)實(shí)例。
總結(jié)在單例模式各種設(shè)計(jì)的方法中,我們使用到了內(nèi)部靜態(tài)類(lèi)的特性,使用了枚舉的特性,所以基礎(chǔ)非常重要,單例模式是設(shè)計(jì)模式之一,而設(shè)計(jì)模式其實(shí)是對(duì)語(yǔ)言特性不足的一面進(jìn)一步的包裝。吸納基礎(chǔ),工作學(xué)習(xí)多加思考,設(shè)計(jì)模式也就自然而然的能夠理解。
另外,好多帖子都說(shuō)利用枚舉的方式在團(tuán)隊(duì)合作中不常使用,因?yàn)樾枰浜希昧嗣杜e別人不熟悉。這點(diǎn)我是不同意的,如果因?yàn)榇蠹也欢皇煜ざ艞壛耸褂煤馨舻奶匦裕敲淳陀肋h(yuǎn)抱著舊的方式停滯不前,JDK的更新也失去了意義。
還是希望能夠不斷的接觸Java新鮮的想法,在深厚的基礎(chǔ)上迸發(fā)不一樣的思路,然后去解決實(shí)際的問(wèn)題。
以上,如有不妥,還望指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/70194.html
摘要:如果是后者,則在執(zhí)行完畢未執(zhí)行之前,被線(xiàn)程二搶占了,這時(shí)已經(jīng)是非了但卻沒(méi)有初始化,所以線(xiàn)程二會(huì)直接返回在之后雙重檢查鎖定才能夠正常達(dá)到單例效果,之前有個(gè)坑。所以,在版本前,雙重檢查鎖形式的單例模式是無(wú)法保證線(xiàn)程安全的。 第一種(懶漢, 線(xiàn)程不安全): public class Singleton { private static Singleton instance; ...
摘要:總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類(lèi)創(chuàng)建型模式共五種工廠(chǎng)方法模式抽象工廠(chǎng)模式單例模式建造者模式原型模式。優(yōu)點(diǎn)一實(shí)例控制單例模式會(huì)阻止其他對(duì)象實(shí)例化其自己的單例對(duì)象的副本,從而確保所有對(duì)象都訪(fǎng)問(wèn)唯一實(shí)例。 總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類(lèi): 創(chuàng)建型模式---共五種:工廠(chǎng)方法模式、抽象工廠(chǎng)模式、單例模式、建造者模式、原型模式。 結(jié)構(gòu)型模式---共七種:適配器模式、裝飾器模式、代理模式、外觀(guān)模式、橋接模式...
摘要:第二種懶漢式線(xiàn)程不安全對(duì)象為空才去實(shí)例化懶漢式是在使用的時(shí)候才會(huì)去加載,不過(guò)當(dāng)多次同時(shí)去加載的時(shí)候就會(huì)存在線(xiàn)程安全問(wèn)題。 單例模式,是一種常用的軟件設(shè)計(jì)模式,在它的核心結(jié)構(gòu)中只包含一個(gè)被稱(chēng)為單例的特殊類(lèi),通過(guò)單例模式可以保證系統(tǒng)中一個(gè)類(lèi)只有一個(gè)實(shí)例。即一個(gè)類(lèi)只有一個(gè)對(duì)象實(shí)例。 第一種:餓漢式 public class SingleEasy { private SingleEas...
摘要:懶漢非線(xiàn)程安全,需要用一定的風(fēng)騷操作控制,裝逼失敗有可能導(dǎo)致看一周的海綿寶寶餓漢天生線(xiàn)程安全,的時(shí)候就已經(jīng)實(shí)例化好,該操作過(guò)于風(fēng)騷會(huì)造成資源浪費(fèi)單例注冊(cè)表初始化的時(shí)候,默認(rèn)單例用的就是該方式特點(diǎn)私有構(gòu)造方法,只能有一個(gè)實(shí)例。 單例設(shè)計(jì)模式(Singleton Pattern)是最簡(jiǎn)單且常見(jiàn)的設(shè)計(jì)模式之一,主要作用是提供一個(gè)全局訪(fǎng)問(wèn)且只實(shí)例化一次的對(duì)象,避免多實(shí)例對(duì)象的情況下引起邏輯性錯(cuò)...
摘要:創(chuàng)建型模式主要有以下五種簡(jiǎn)單工廠(chǎng)模式和工廠(chǎng)方法模式抽象工廠(chǎng)模式單例模式建造者模式原型模式在設(shè)計(jì)模式一書(shū)中將工廠(chǎng)模式分為兩類(lèi)工廠(chǎng)方法模式與抽象工廠(chǎng)模式。 一、 設(shè)計(jì)模式(Design pattern)是什么 設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉、經(jīng)過(guò)分類(lèi)編目的代碼設(shè)計(jì)的經(jīng)驗(yàn)總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 二、 為什么會(huì)有設(shè)計(jì)模式 在軟件開(kāi)發(fā)過(guò)...
閱讀 1163·2021-11-24 09:38
閱讀 3610·2021-11-22 15:32
閱讀 3461·2019-08-30 15:54
閱讀 2574·2019-08-30 15:53
閱讀 1499·2019-08-30 15:52
閱讀 2540·2019-08-30 13:15
閱讀 1843·2019-08-29 12:21
閱讀 1405·2019-08-26 18:36