設(shè)計(jì)模式系列——單例模式

1.餓漢式單例類

class EagerSingleton {   
    private static final EagerSingleton instance = new EagerSingleton();   
    private EagerSingleton() { }   
  
    public static EagerSingleton getInstance() {  
        return instance;   
    }     
}  

當(dāng)類被加載時(shí)亡驰,靜態(tài)變量instance會(huì)被初始化,此時(shí)類的私有構(gòu)造函
數(shù)會(huì)被調(diào)用,單例類的唯一實(shí)例將被創(chuàng)建本冲。如果使用餓漢式單例來實(shí)現(xiàn)EagerSingleton類的設(shè)計(jì),則不會(huì)出現(xiàn)創(chuàng)建多個(gè)單例對(duì)象的情況劫扒,可確保單例對(duì)象的唯一性檬洞。

2.懶漢式單例類與線程鎖定

class LazySingleton {   
    private static LazySingleton instance = null;   
  
    private LazySingleton() { }   
  
    synchronized public static LazySingleton getInstance() {   
        if (instance == null) {  
            instance = new LazySingleton();   
        }  
        return instance;   
    }  
}  

該懶漢式單例類在getInstance()方法前面增加了關(guān)鍵字synchronized進(jìn)行線程鎖,以處理多個(gè)線程同時(shí)訪問的問題沟饥。但是添怔,上述代碼雖然解決了線程安全問題,但是每次調(diào)用getInstance()時(shí)都需要進(jìn)行線程鎖定判斷贤旷,在多線程高并發(fā)訪問環(huán)境中广料,將會(huì)導(dǎo)致系統(tǒng)性能大大降低。如何既解決線程安全問題又不影響系統(tǒng)性能呢幼驶?我們繼續(xù)對(duì)懶漢式單例進(jìn)行改進(jìn)艾杏。事實(shí)上,我們無須對(duì)整個(gè)getInstance()方法進(jìn)行鎖定盅藻,只需對(duì)其中的代碼“instance = new LazySingleton();”進(jìn)行鎖定即可购桑。因此getInstance()方法可以進(jìn)行如下改進(jìn):

public static LazySingleton getInstance() {   
    if (instance == null) {  
        synchronized (LazySingleton.class) {  
            instance = new LazySingleton();   
        }  
    }  
    return instance;   
}  

問題貌似得以解決,事實(shí)并非如此氏淑。如果使用以上代碼來實(shí)現(xiàn)單例勃蜘,還是會(huì)存在單例對(duì)象不唯一。原因如下:
假如在某一瞬間線程A和線程B都在調(diào)用getInstance()方法假残,此時(shí)instance對(duì)象為null值缭贡,均能通過instance == null的判斷。由于實(shí)現(xiàn)了synchronized加鎖機(jī)制辉懒,線程A進(jìn)入synchronized鎖定的代碼中執(zhí)行實(shí)例創(chuàng)建代碼阳惹,線程B處于排隊(duì)等待狀態(tài),必須等待線程A執(zhí)行完畢后才可以進(jìn)入synchronized鎖定代碼眶俩。但當(dāng)A執(zhí)行完畢時(shí)莹汤,線程B并不知道實(shí)例已經(jīng)創(chuàng)建,將繼續(xù)創(chuàng)建新的實(shí)例仿便,導(dǎo)致產(chǎn)生多個(gè)單例對(duì)象体啰,違背單例模式的設(shè)計(jì)思想攒巍,因此需要進(jìn)行進(jìn)一步改進(jìn),在synchronized中再進(jìn)行一次(instance == null)判斷荒勇,這種方式稱為雙重檢查鎖定(Double-Check Locking)柒莉。使用雙重檢查鎖定實(shí)現(xiàn)的懶漢式單例類完整代碼如下所示:

class LazySingleton {   
    private volatile static LazySingleton instance = null;   
  
    private LazySingleton() { }   
  
    public static LazySingleton getInstance() {   
        //第一重判斷  
        if (instance == null) {  
            //鎖定代碼塊  
            synchronized (LazySingleton.class) {  
                //第二重判斷  
                if (instance == null) {  
                    instance = new LazySingleton(); //創(chuàng)建單例實(shí)例  
                }  
            }  
        }  
        return instance;   
    }  
}  

需要注意的是,如果使用雙重檢查鎖定來實(shí)現(xiàn)懶漢式單例類沽翔,需要在靜態(tài)成員變量instance之前增加修飾符volatile兢孝,被volatile修飾的成員變量可以確保多個(gè)線程都能夠正確處理,且該代碼只能在JDK 1.5及以上版本中才能正確執(zhí)行仅偎。由于volatile關(guān)鍵字會(huì)屏蔽Java虛擬機(jī)所做的一些代碼優(yōu)化跨蟹,可能會(huì)導(dǎo)致系統(tǒng)運(yùn)行效率降低,因此即使使用雙重檢查鎖定來實(shí)現(xiàn)單例模式也不是一種完美的實(shí)現(xiàn)方式橘沥。

3.餓漢式單例類與懶漢式單例類比較

餓漢式單例類在類被加載時(shí)就將自己實(shí)例化窗轩,它的優(yōu)點(diǎn)在于無須考慮多線程訪問問題,可以確保實(shí)例的唯一性座咆;從調(diào)用速度和反應(yīng)時(shí)間角度來講痢艺,由于單例對(duì)象一開始就得以創(chuàng)建,因此要優(yōu)于懶漢式單例介陶。但是無論系統(tǒng)在運(yùn)行時(shí)是否需要使用該單例對(duì)象堤舒,由于在類加載時(shí)該對(duì)象就需要?jiǎng)?chuàng)建,因此從資源利用效率角度來講哺呜,餓漢式單例不及懶漢式單例舌缤,而且在系統(tǒng)加載時(shí)由于需要?jiǎng)?chuàng)建餓漢式單例對(duì)象,加載時(shí)間可能會(huì)比較長(zhǎng)某残。
懶漢式單例類在第一次使用時(shí)創(chuàng)建国撵,無須一直占用系統(tǒng)資源,實(shí)現(xiàn)了延遲加載驾锰,但是必須處理好多個(gè)線程同時(shí)訪問的問題卸留,特別是當(dāng)單例類作為資源控制器走越,在實(shí)例化時(shí)必然涉及資源初始化椭豫,而資源初始化很有可能耗費(fèi)大量時(shí)間,這意味著出現(xiàn)多線程同時(shí)首次引用此類的機(jī)率變得較大旨指,需要通過雙重檢查鎖定等機(jī)制進(jìn)行控制赏酥,這將導(dǎo)致系統(tǒng)性能受到一定影響。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谆构,一起剝皮案震驚了整個(gè)濱河市裸扶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搬素,老刑警劉巖呵晨,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魏保,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡摸屠,警方通過查閱死者的電腦和手機(jī)谓罗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來季二,“玉大人檩咱,你說我怎么就攤上這事】柘希” “怎么了刻蚯?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)桑嘶。 經(jīng)常有香客問我炊汹,道長(zhǎng),這世上最難降的妖魔是什么逃顶? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任兵扬,我火速辦了婚禮,結(jié)果婚禮上口蝠,老公的妹妹穿的比我還像新娘器钟。我一直安慰自己,他們只是感情好妙蔗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布傲霸。 她就那樣靜靜地躺著,像睡著了一般眉反。 火紅的嫁衣襯著肌膚如雪昙啄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天寸五,我揣著相機(jī)與錄音梳凛,去河邊找鬼。 笑死梳杏,一個(gè)胖子當(dāng)著我的面吹牛韧拒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播十性,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼叛溢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了劲适?” 一聲冷哼從身側(cè)響起楷掉,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霞势,沒想到半個(gè)月后烹植,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斑鸦,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年草雕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鄙才。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡促绵,死狀恐怖攒庵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情败晴,我是刑警寧澤浓冒,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站尖坤,受9級(jí)特大地震影響稳懒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慢味,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一场梆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纯路,春花似錦或油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叫编,卻和暖如春辖佣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搓逾。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工卷谈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霞篡。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓世蔗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親寇损。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凸郑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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