設(shè)計模式系列-單例模式的N種實現(xiàn)與總結(jié)

通常Java實現(xiàn)單例模式有很多種方式涨颜,大致可分為懶漢模式餓漢模式实蔽,其主要區(qū)別是實例延遲加載的問題上炎,當(dāng)然單例模式往往也關(guān)注其他問題寿冕,如:線程安全等蕊程。下面試圖來總結(jié)單例模式的這些注意點。

代碼地址:GitHub

餓漢模式


public class Singleton {
    private Singleton(){}
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

餓漢模式在類加載時候就實例化對象驼唱,使用時直接調(diào)用getInstance()方法藻茂。這個模式下,是線程安全的玫恳,在多線程并發(fā)模式下不會重復(fù)實例化對象辨赐。
缺點:對象過早的實例化,浪費系統(tǒng)資源京办。

懶漢模式

public class Singleton {
    private Singleton(){}
    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
} 

這種模式下在類加載時候并沒有實例化對象掀序,而是在調(diào)用getInstance()方法。之所以使用懶漢模式惭婿,是為了避免多早的實例化對象不恭,從而浪費系統(tǒng)資源。
缺點:僅適用于單線程审孽,線程不安全县袱。

改進1 - 引入synchronized

public class Singleton {
    private Singleton(){}
    private static Singleton instance = null;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
} 

之所以引入synchronized修飾getInstance()方法,是為了解決線程不安全的問題佑力。利用多線程同步機制式散,讓原先的線程不安全回歸到線程安全。但引入synchronized會因為線程阻塞打颤、切換會帶一些不必要的開銷暴拄,從而降低系統(tǒng)性能漓滔。

改進2 - 雙重檢查鎖定

public class Singleton {
    private Singleton(){}
    private static volatile Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {              // A
            synchronized (Singleton.class) {
                if (instance == null) {      // B
                    instance = new Singleton(); // C
                }
            }
        }
        return instance;
    }
} 

對比改進1中,可以看到synchronized不再修飾一個方法乖篷,而是縮減到修改代碼塊响驴,因為加鎖同步的話,范圍越小撕蔼,性能影響最小豁鲤。

這里可以注意到修飾變量instance的關(guān)鍵字增加了volatile。這里volatile主要作用是提供內(nèi)存屏障鲸沮,禁止指令重排序琳骡。

現(xiàn)有t1、t2兩個線程同時訪問getInstance(),假設(shè)t1讼溺、t2都執(zhí)行到A處楣号。由于有同步鎖,只能有個1個線程獲得鎖怒坯,假如t1擁有該同步鎖炫狱,t1執(zhí)行到C處instace = new Singleton()。將會做如下3步驟:
1.分配內(nèi)存空間
2.初始化
3.將instance指向分配內(nèi)存空間
正常的執(zhí)行順序應(yīng)為:1->2->3剔猿。執(zhí)行第3步時视译,這時候的instance就不再是null了。但由于指令重排序的存在归敬,執(zhí)行順序有可能變化為:1->3->2憎亚。當(dāng)執(zhí)行3的時候,instance就不再是null弄慰,但初始化工作有可能還沒有做完第美。這時候如果t2獲取鎖執(zhí)行的話,就會直接獲取有可能還沒有初始化完成的instance陆爽。這樣使用instance會引起程序報錯什往。當(dāng)然這也是極端情況下,我嘗試幾次無法捕捉重現(xiàn)慌闭,但并不意味著問題不存在别威。volatile當(dāng)然還是要加的。

Aif判斷作用主要是防止過多是線程執(zhí)行同步代碼塊驴剔;如果是單例模式的話省古,這里同步代碼塊只會被執(zhí)行一次。Bif判斷作用主要是防止多線程作用下重復(fù)實例化丧失,保證線程安全豺妓。這也被稱為:雙重檢查鎖定

雙重檢查鎖定屬于一種兼顧線程安全和性能的實現(xiàn)。

改進3 - 靜態(tài)內(nèi)部類

public class Singleton {
    private Singleton(){}
    private static class Holder {
        public static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.instance; // 執(zhí)行Holder的初始化工作
    }
}

使用靜態(tài)內(nèi)部類也是懶漢模式的一種實現(xiàn)琳拭,當(dāng)調(diào)用ggetInstance()才會觸發(fā)加載靜態(tài)內(nèi)部類训堆,從而初始化獲取instance實例。利用靜態(tài)內(nèi)部類的加載機制來保證線程安全白嘁。

枚舉方式


public enum Singleton {
    INSTANCE;
    Singleton(){}
    
    public Singleton getInstance() {
        return INSTANCE;
    }

}

用枚舉方式實現(xiàn)單例模式坑鱼,是目前比較推薦的。枚舉方式的好處是:1絮缅、線程安全未状;2叁熔、防止反射出現(xiàn)多個實例喂链;3节沦、防止反序列化出現(xiàn)多個實例。

以上是關(guān)于java單例模式的一些總結(jié)屎开,如有紕漏,還請指出马靠。

代碼地址:GitHub

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奄抽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子甩鳄,更是在濱河造成了極大的恐慌逞度,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妙啃,死亡現(xiàn)場離奇詭異档泽,居然都是意外死亡,警方通過查閱死者的電腦和手機揖赴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進店門馆匿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人燥滑,你說我怎么就攤上這事渐北。” “怎么了铭拧?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵赃蛛,是天一觀的道長。 經(jīng)常有香客問我搀菩,道長呕臂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任肪跋,我火速辦了婚禮歧蒋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己疏尿,他們只是感情好瘟芝,可當(dāng)我...
    茶點故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著褥琐,像睡著了一般锌俱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敌呈,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天贸宏,我揣著相機與錄音,去河邊找鬼磕洪。 笑死吭练,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的析显。 我是一名探鬼主播鲫咽,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谷异!你這毒婦竟也來了分尸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤歹嘹,失蹤者是張志新(化名)和其女友劉穎箩绍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尺上,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡材蛛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怎抛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卑吭。...
    茶點故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖马绝,靈堂內(nèi)的尸體忽然破棺而出陨簇,到底是詐尸還是另有隱情,我是刑警寧澤迹淌,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布河绽,位于F島的核電站,受9級特大地震影響唉窃,放射性物質(zhì)發(fā)生泄漏耙饰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一纹份、第九天 我趴在偏房一處隱蔽的房頂上張望苟跪。 院中可真熱鬧廷痘,春花似錦、人聲如沸件已。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篷扩。三九已至兄猩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鉴未,已是汗流浹背枢冤。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铜秆,地道東北人淹真。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像连茧,于是被迫代替她去往敵國和親核蘸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,442評論 2 359

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