Java 設(shè)計(jì)模式 -- 單例模式

  1. 有些時(shí)候赊淑,我們想要一個(gè)類在整個(gè)系統(tǒng)中僅存在一個(gè)實(shí)例本砰。比如說碴裙,系統(tǒng)給我們提供了一個(gè)打印機(jī)硬件設(shè)施,但是我們?cè)谙到y(tǒng)中多次new 打印機(jī)点额,創(chuàng)建出多個(gè)打印機(jī)的實(shí)例去完成打印任務(wù)舔株,那么這個(gè)時(shí)候就會(huì)出現(xiàn)資源沖突現(xiàn)象,這就要求我們必須想一個(gè)辦法还棱,去確保系統(tǒng)中存在唯一的一個(gè)打印機(jī)實(shí)例载慈,解決方法有很多種,比如
  • 創(chuàng)建一個(gè)全局變量
public static Printer mPrinter = new Printer();

這樣創(chuàng)建一個(gè)全局變量珍手,并且規(guī)定想要使用打印機(jī)服務(wù)办铡,必須使用mPrinter 這個(gè)實(shí)例去完成叶骨,不得再去創(chuàng)建新的打印機(jī)實(shí)例像寒,這種方法是可以解決這個(gè)問題的。但是這種方法是有缺陷的琅翻,比如焙蹭,一些不遵守規(guī)則的人或者說不明白規(guī)則的人又創(chuàng)建了一個(gè)Printer實(shí)例晒杈,還是會(huì)引起沖突,這就出現(xiàn)了單例模式孔厉。

  1. 單例模式
單例模式UML

如圖示拯钻,如若我們將單例模式的構(gòu)造方法設(shè)置成private,也就是私有的撰豺,只提供給一個(gè)獲得實(shí)例的接口粪般,并且在getInstance 方法中做些特殊處理,就可以保證系統(tǒng)中存在唯一實(shí)例污桦,下面介紹幾種單例模式的實(shí)現(xiàn)方法亩歹。

  • 經(jīng)典實(shí)現(xiàn)方法(懶漢式)
public class Printer {
    
    private static Printer mPrinter = null;
    
    private Printer(){
        
    }
    
    public static Printer getInstance(){
        
        if(null == mPrinter){
            mPrinter = new Printer();
        }
        
        return mPrinter;
    }

}

通過這種定義方法,就可以保證系統(tǒng)中僅存在唯一實(shí)例凡橱,是嗎小作?
一般情況下,這種方式是不會(huì)存在問題的稼钩,但是因?yàn)槎嗑€程顾稀,導(dǎo)致這種方法是不可靠的。舉例來說坝撑,如果系統(tǒng)中存在兩個(gè)子線程静秆,同時(shí)進(jìn)入 getInstance() 函數(shù)粮揉,當(dāng)一個(gè)線程剛剛通過if判斷語句,還未來得及 new 出一個(gè)實(shí)例抚笔,線程切換扶认,另一個(gè)線程也通過了if判斷語句,那么這個(gè)時(shí)候就會(huì)new出兩個(gè)實(shí)例塔沃,還是無法保證在多線程條件下的單一性蝠引,所以我們需要在getInstance() 函數(shù)前加入 synchronized 關(guān)鍵字。


    public static synchronized Printer getInstance(){
        
        if(null == mPrinter){
            mPrinter = new Printer();
        }
        
        return mPrinter;
    }

synchronized 處理蛀柴,可以保證同一時(shí)刻螃概,只有一個(gè)線程可以進(jìn)入getInstance() 函數(shù),但是如果我們需要多次調(diào)用getInstance() 函數(shù)時(shí)鸽疾,這種方法是非常消耗資源的吊洼。當(dāng)然,如果我們只是很少次使用這個(gè)函數(shù)制肮,那么這種方法是完全滿足需求的冒窍。那么針對(duì)多次使用的情況,我們只能使用下一種方式了豺鼻。

  • 餓漢式
public class Printer {
    
    private static Printer mPrinter = new Printer();
    
    private Printer(){
        
    }
    
    public static Printer getInstance(){
        return mPrinter;
    }

}

這種方法我們直接在定義的時(shí)候就 new 出了一個(gè)實(shí)例综液,無論調(diào)用getInstance() 多少次,我們也只會(huì)返回固定的一個(gè)實(shí)例儒飒,也就不存在多線程同步的問題了谬莹。

但是,如果我們從頭到位并沒有用到 Printer 桩了,而我們卻草率的 new 出了一個(gè)實(shí)例附帽,這樣會(huì)占用內(nèi)存資源,所以我們還要用到下一種方法井誉。

  • 雙重檢查鎖定
public class Printer {
    
    private static volatile Printer mPrinter = null;
    
    private Printer(){
        
    }
    
    public static Printer getInstance(){
        if(null == mPrinter){
            
            synchronized (Printer.class){
                
                if(null == mPrinter){
                    
                    mPrinter = new Printer();
                }
            }
        }
        
        return mPrinter;
    }

}

當(dāng)一個(gè)線程通過第一個(gè)if判斷語句時(shí)蕉扮,無論怎樣,在同一時(shí)刻颗圣,只有一個(gè)線程可以進(jìn)入第二個(gè)if判斷語句喳钟,而且會(huì)new 出一個(gè)實(shí)例,保證synchronized方法從始至終只會(huì)執(zhí)行一次在岂,這樣就解決了餓漢式和懶漢式的尷尬荚藻,因?yàn)樗炔粫?huì)像懶漢式那樣多次進(jìn)入synchronized方法占用系統(tǒng)資源,也不會(huì)像餓漢式那樣在不使用的情況下占用系統(tǒng)資源洁段。

  • 一種更好的實(shí)現(xiàn)方法

事實(shí)上還存在一種能夠?qū)I漢式和懶漢式的缺點(diǎn)全部克服而且能將二者的優(yōu)點(diǎn)合二為一的方法,那就是 Initialization on Demand Holder(IoDH)方法共郭。

public class Printer {
    
    private Printer(){
        
    }
    
    public static Printer newInstance(){
        return CreatePrinter.mPrinter;
    }
    
    private static class CreatePrinter{
        private final static Printer mPrinter = new Printer();
    }

}

因?yàn)殪o態(tài)的單例對(duì)象沒有作為類的成員變量直接實(shí)例化祠丝,因此在Printer類加載時(shí)并沒有實(shí)例化mPrinter疾呻。第一次調(diào)用newInstance() 的時(shí)候加載內(nèi)部類CreatePrinter ,該內(nèi)部類定義了一個(gè)static 類型的變量mPrinter写半,此時(shí)會(huì)首先初始化這個(gè)變量岸蜗,由JVM 來保證其線程安全性,確保該成員變量只被實(shí)例化一次叠蝇。

可見璃岳,通過使用這個(gè)方法,不僅實(shí)現(xiàn)了延遲加載悔捶,又可以保證線程安全铃慷,不影響系統(tǒng)性能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜕该,一起剝皮案震驚了整個(gè)濱河市犁柜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堂淡,老刑警劉巖馋缅,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绢淀,居然都是意外死亡萤悴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門皆的,熙熙樓的掌柜王于貴愁眉苦臉地迎上來覆履,“玉大人,你說我怎么就攤上這事祭务∧诠罚” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵义锥,是天一觀的道長柳沙。 經(jīng)常有香客問我,道長拌倍,這世上最難降的妖魔是什么赂鲤? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮柱恤,結(jié)果婚禮上数初,老公的妹妹穿的比我還像新娘。我一直安慰自己梗顺,他們只是感情好泡孩,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寺谤,像睡著了一般仑鸥。 火紅的嫁衣襯著肌膚如雪吮播。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天眼俊,我揣著相機(jī)與錄音意狠,去河邊找鬼。 笑死疮胖,一個(gè)胖子當(dāng)著我的面吹牛环戈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播澎灸,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼院塞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了击孩?” 一聲冷哼從身側(cè)響起迫悠,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巩梢,沒想到半個(gè)月后创泄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡括蝠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年鞠抑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忌警。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搁拙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出法绵,到底是詐尸還是另有隱情箕速,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布朋譬,位于F島的核電站盐茎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏徙赢。R本人自食惡果不足惜字柠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狡赐。 院中可真熱鬧窑业,春花似錦、人聲如沸枕屉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至西潘,卻和暖如春铜异,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秸架。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咆蒿,地道東北人东抹。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像沃测,于是被迫代替她去往敵國和親缭黔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 概念 java中單例模式是一種常見的設(shè)計(jì)模式蒂破,單例模式的寫法有好幾種馏谨,比較常見的有:懶漢式單例、餓漢式單例附迷。單例模...
    怡紅快綠閱讀 463評(píng)論 0 0
  • 單例模式由于只創(chuàng)建了唯一對(duì)象可以避免資源的多重占用惧互,減少內(nèi)存的開銷,對(duì)于經(jīng)常性使用對(duì)象的類來說喇伯,單例是一個(gè)不錯(cuò)的選...
    InBinfen閱讀 392評(píng)論 2 2
  • 今天筆試的時(shí)候被問到了單例模式,聽了很多次,但是卻沒有認(rèn)真看過,所以交了白卷,懶的教訓(xùn)啊!還有一題比較有趣的題目:...
    shakesbears閱讀 375評(píng)論 0 3
  • Java設(shè)計(jì)模式——單例模式 單例模式應(yīng)該是大家最為熟知的一種設(shè)計(jì)模式了喊儡,相信大家或多或少的都在自己的項(xiàng)目中使用過...
    gogoingmonkey閱讀 516評(píng)論 0 2
  • 很小的時(shí)候,應(yīng)該還是學(xué)齡前吧稻据,外婆給我講過一個(gè)故事艾猜。一個(gè)餐廳很厲害,門口掛了無數(shù)的招牌吹噓自己什么菜都會(huì)做捻悯。有一天...
    北美之北閱讀 2,465評(píng)論 4 4