1.5 七種單例模式

七種單例模式

什么是設(shè)計模式呕乎?簡單的理解就是前人留下來的一些經(jīng)驗總結(jié)而已昔汉,然后把這些經(jīng)驗起了個名字叫Design Pattern让网,翻譯過來就是設(shè)計模式蛤签,通過使用設(shè)計模式可以讓我們的代碼復(fù)用性更高,可維護性更高栅哀,讓你的代碼寫的更優(yōu)雅震肮。設(shè)計模式理論上有23種,今天就先來學(xué)習(xí)下最常用的單例模式留拾。

單例模式

保證一個類僅有一個實例戳晌,并提供一個訪問它的全局訪問點。

寫單例模式有7中方法痴柔,各有利弊沦偎,下面逐個分析

1、餓漢式

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

這種方式創(chuàng)建的單例和名字很貼切咳蔚,饑不擇食豪嚎,在JVM加載類的時候就創(chuàng)建了實例對象,不管你用還是不用谈火,先創(chuàng)建了再說侈询。
如果一直沒有使用,便浪費了空間糯耍,典型的空間換時間的做法扔字,每次調(diào)用的時候,就不需要再判斷温技,節(jié)省了運行時間革为。

餓漢式單例實現(xiàn)比較簡單,單例對象在初始化的時候就創(chuàng)建了舵鳞,適合單例對象占用內(nèi)存比較小的情況震檩。

但是,如果單例初始化操作比較耗時系任,或者單例占用內(nèi)存比較大恳蹲,或者單例在某種特定情況才會使用虐块,一般情況不會使用,這個時候使用餓漢式單例模式就不適合嘉蕾。需要使用懶漢式單例按需延時加載贺奠。

2、餓漢式(非線程安全)

public class SingletonLazyNotSafe {

    private static SingletonLazyNotSafe instance = null;

    private SingletonLazyNotSafe() {

    }


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

這種方式創(chuàng)建的單例對象错忱,因為聲明了一個靜態(tài)對象該值為空儡率,在JVM加載類的時候,并不會創(chuàng)建實例對象以清,節(jié)省了資源儿普,只有在第一次調(diào)用getInstance()才會初始化對象。

但是這種模式的不能工作在多線程中掷倔,在多線程調(diào)用getInstance()可能會產(chǎn)生多個實例對象眉孩。

3、懶漢式(線程安全)

public class SingletonSafe {

    private static SingletonSafe instance = null;

    private SingletonSafe() {

    }

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

這種單例模式在getInstance()的方法前面加上了synchronized關(guān)鍵字勒葱,用來保證多線程訪問情況下加上同步鎖浪汪,將其它線程阻塞在同步鎖外面,這樣保證獲取到是單個實例對象凛虽。

4死遭、雙重校驗鎖(DCL)

上面的單例模式在多線程調(diào)用getInstance()會產(chǎn)生性能問題。于是有了DCL單例模式凯旋。

DCL 是Double Check Lock的縮寫呀潭。雙重校驗鎖的意思是,只要保證在 檢測null的操作和創(chuàng)建對象的操作時候至非,這兩個操作能夠原子地進行钠署,那么單例就已經(jīng)保證了。

public class SingletonDCL {

    private static volatile SingletonDCL instance;

    private SingletonDCL() {

    }

    public static SingletonDCL getInstance() {

        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

這種寫法在getSingleton()方法中對singleton進行了兩次判空荒椭,第一次是為了不必要的同步踏幻,第二次是在singleton等于null的情況下才創(chuàng)建實例。在這里用到了volatile關(guān)鍵字戳杀。

這種寫法该面,在JDK5之前是不安全的:

  1. java的new不是原子的,具體在更細的層面還是有諸多步驟:
    • 分配新對象的內(nèi)存
    • 調(diào)用類的構(gòu)造器信卡,初始化成員字段
    • instance被賦為指向新的對象的引用隔缀。

說白了,JDK5之前不能保證有volatile修飾的對象構(gòu)造內(nèi)部是有序的傍菇。

  1. 線程A發(fā)現(xiàn)instance沒有被實例化猾瘸,它獲得鎖,然后去實例化該對象,JVM容許在沒有完全實例化完成的時候牵触,將實例的指針賦給這個instance變量淮悼,而此時instance==null就為false了,在初始化完成之前揽思,線程B進入此方法袜腥,發(fā)現(xiàn)instance不為空,認為已經(jīng)初始化完成了钉汗,于是便使用了這個尚未完全初始化的實例對象羹令,可能引起其他的異常。

雙重校驗鎖:既可以達到線程安全损痰,也可以使性能不受很大的影響福侈,換句話說在保證線程安全的前提下,既節(jié)省空間也節(jié)省了時間卢未,集合了「餓漢式」和兩種「懶漢式」的優(yōu)點肪凛,取其精華,去其槽粕辽社。

5显拜、靜態(tài)內(nèi)部類

采用內(nèi)部類,在這個類里面去創(chuàng)建實例對象爹袁,只要程序中不使用內(nèi)部類JVM就不會去加載這個單例類,也就不會創(chuàng)建單例對象矮固。從而實現(xiàn)「懶漢式」的延遲加載和線程安全失息。

public class SingletonInner {

    private SingletonInner() {

    }

    public static SingletonInner getInstance() {
        return SingletonInnerHolder.instance;
    }

    private static class SingletonInnerHolder{
        private static final SingletonInner instance = new SingletonInner();
    }
}

第一次加載Singleton類時并不會初始化sInstance,只有第一次調(diào)用getInstance方法時虛擬機加載SingletonHolder 并初始化sInstance 档址,這樣不僅能確保線程安全也能保證Singleton類的唯一性盹兢,所以推薦使用靜態(tài)內(nèi)部類單例模式。

然而這還不是最簡單的方式守伸,《Effective Java》中作者推薦了一種更簡潔方便的使用方式绎秒,就是使用「枚舉」。

6尼摹、枚舉

用枚舉來實現(xiàn)單實例控制會更加簡潔见芹,而且無償?shù)靥峁┝诵蛄谢瘷C制,并由JVM從根本上提供保障蠢涝,絕對防止多次實例化玄呛,是更簡潔、高效和二、安全的實現(xiàn)單例的方式徘铝。

public enum  SingletonEnum {
    INSTANCE;

}

7、使用容器

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

這種事用SingletonManager 將多種單例類統(tǒng)一管理,在使用時根據(jù)key獲取對象對應(yīng)類型的對象惕它。這種方式使得我們可以管理多種類型的單例怕午,并且在使用時可以通過統(tǒng)一的接口進行獲取操作,降低了用戶的使用成本淹魄,也對用戶隱藏了具體實現(xiàn)郁惜,降低了耦合度。

參考文章:

【Java】設(shè)計模式:深入理解單例模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揭北,一起剝皮案震驚了整個濱河市扳炬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搔体,老刑警劉巖恨樟,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疚俱,居然都是意外死亡劝术,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門呆奕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來养晋,“玉大人,你說我怎么就攤上這事梁钾∩” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵姆泻,是天一觀的道長零酪。 經(jīng)常有香客問我,道長四苇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任方咆,我火速辦了婚禮月腋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓣赂。我一直安慰自己榆骚,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布煌集。 她就那樣靜靜地躺著寨躁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牙勘。 梳的紋絲不亂的頭發(fā)上职恳,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天所禀,我揣著相機與錄音,去河邊找鬼放钦。 笑死色徘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的操禀。 我是一名探鬼主播褂策,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颓屑!你這毒婦竟也來了斤寂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揪惦,失蹤者是張志新(化名)和其女友劉穎遍搞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體器腋,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡溪猿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纫塌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诊县。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖措左,靈堂內(nèi)的尸體忽然破棺而出依痊,到底是詐尸還是另有隱情,我是刑警寧澤怎披,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布胸嘁,位于F島的核電站,受9級特大地震影響钳枕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赏壹,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一鱼炒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝌借,春花似錦昔瞧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稍坯,卻和暖如春酬荞,著一層夾襖步出監(jiān)牢的瞬間搓劫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工混巧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枪向,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓咧党,卻偏偏與公主長得像秘蛔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子傍衡,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

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