單例模式——應(yīng)用最廣的模式

導(dǎo)語(yǔ)

單例模式是應(yīng)用最廣的模式妓蛮,在應(yīng)用這個(gè)模式時(shí)恶迈,單例對(duì)象的類必須保證只有一個(gè)實(shí)例存在触菜。許多時(shí)候整個(gè)系統(tǒng)只需要擁有一個(gè)全局對(duì)象,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為期揪。

主要內(nèi)容

  • 單例模式的定義
  • 單例模式的使用場(chǎng)景
  • 單例模式的UML類圖
  • 單例模式的實(shí)現(xiàn)方式

具體內(nèi)容

單例模式的定義

確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例椒袍。

單例模式的使用場(chǎng)景

他的作用是確保某個(gè)類只有一個(gè)實(shí)例坛吁,避免產(chǎn)生多個(gè)對(duì)象消耗過(guò)多的資源,或者某個(gè)類型的對(duì)象只應(yīng)該有一個(gè)彤委。例如鞭铆,創(chuàng)建一個(gè)對(duì)象需要消耗的資源過(guò)多,如要訪問(wèn)IO和數(shù)據(jù)庫(kù)等資源葫慎,這時(shí)就要考慮使用單例模式衔彻。

單例模式的UML類圖

單例模式的UML類圖如下所示。

單例模式的UML類圖

角色介紹:

  • Client——高層客戶端偷办。
  • Singleton——單例類艰额。

實(shí)現(xiàn)單例模式主要關(guān)鍵點(diǎn):

  • 構(gòu)造函數(shù)不對(duì)外開(kāi)放,一般為Private椒涯。
  • 通過(guò)一個(gè)靜態(tài)方法或者枚舉返回單例類對(duì)象柄沮。
  • 確保單例類的對(duì)象有且只有一個(gè),尤其是在多線程環(huán)境下废岂。
  • 確保單例類對(duì)象在反序列化時(shí)不會(huì)重新構(gòu)建對(duì)象祖搓。

單例模式的實(shí)現(xiàn)方式

單例模式的實(shí)現(xiàn)有如下幾種:
推薦使用DCL單例(雙重檢查鎖定)和靜態(tài)內(nèi)部類單例模式。

  • 餓漢模式
  • 懶漢模式(線程不安全)
  • 懶漢模式(線程安全)
  • DCL單例(雙重檢查鎖定)
  • 靜態(tài)內(nèi)部類單例模式
  • 枚舉單例
  • 使用容器實(shí)現(xiàn)單例模式
餓漢模式
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton(){
    }

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

這種寫法是在類裝載時(shí)就實(shí)例化instance湖苞,他避免了多線程的同步問(wèn)題拯欧。但是不能保證有別的方式去裝載,沒(méi)有達(dá)到懶加載(延遲加載)财骨。

懶漢模式(線程不安全)
public class Singleton {
    private static Singleton instance;

    private Singleton (){
    }

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

達(dá)到了懶加載镐作,但是在多線程不能正常工作。

懶漢模式(線程安全)
public class Singleton {
    private static Singleton instance;

    private Singleton(){
    }

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

這種寫法能夠在多線程中很好的工作隆箩,但是每次調(diào)用getInstance方法都會(huì)進(jìn)行同步该贾,反應(yīng)稍慢,還會(huì)造成不必要的開(kāi)銷捌臊,所以者這種不建議使用杨蛋。

DCL單例(雙重檢查鎖定)
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton(){
    }

    public static Singleton getSingleton() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

這種寫法在getSingleton方法中對(duì)singleton進(jìn)行了兩次判空,第一次是為了不必要的同步理澎,第二次是為了在null的情況下創(chuàng)建實(shí)例逞力。我們會(huì)發(fā)現(xiàn)上面代碼有一個(gè)volatile關(guān)鍵字,因?yàn)樵谶@里會(huì)有DCL失效問(wèn)題糠爬,原因是Java編譯器允許處理器亂序執(zhí)行掏击。那么為了解決這個(gè)問(wèn)題,在JDK1.5之后秩铆,具體化了volatile關(guān)鍵字砚亭,只要定義時(shí)加上他灯变,可以保證執(zhí)行的順序,雖然會(huì)影響性能捅膘。這種方式第一次加載時(shí)會(huì)稍慢添祸,在高并發(fā)環(huán)境會(huì)有缺陷,但是一般能夠滿足需求寻仗。

靜態(tài)內(nèi)部類單例模式
public class Singleton {
    private Singleton(){
    }

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
    *靜態(tài)內(nèi)部類
    */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

這種是推薦使用的單例模式實(shí)現(xiàn)方式刃泌。當(dāng)?shù)谝淮渭虞dSingleton類時(shí)并不會(huì)初始化INSTANCE,只有在第一次調(diào)用getInstance方法時(shí)才會(huì)導(dǎo)致INSTANCE被初始化署尤。這種方式不僅能夠保證線程安全耙替,也能保證單例對(duì)象的唯一性,同時(shí)也延長(zhǎng)了單例的實(shí)例化曹体。

使用容器實(shí)現(xiàn)單例模式
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) ;
    }
}

將多種單例類型注入到一個(gè)統(tǒng)一的管理類中俗扇,在使用時(shí)根據(jù)key獲取對(duì)象對(duì)應(yīng)類型的對(duì)象。這種方式使得我們可以管理多種類型的單例箕别,并且在使用時(shí)可以通過(guò)統(tǒng)一的接口進(jìn)行獲取操作铜幽,降低了用戶的使用成本,也對(duì)用戶隱藏了具體實(shí)現(xiàn)串稀,降低了耦合度除抛。

總結(jié)

單例模式是運(yùn)用頻率很高的模式,但是母截,由于在客戶端通常沒(méi)有高并發(fā)的情況到忽,因此,選擇哪種實(shí)現(xiàn)方式都不會(huì)有太大的影響清寇。即使如此喘漏,出于效率考慮,推薦使用DCL單例(雙重檢查鎖定)和靜態(tài)內(nèi)部類單例模式颗管。

  • 優(yōu)點(diǎn):

    • 由于單例模式在內(nèi)存中只有一個(gè)實(shí)例,減少了內(nèi)存開(kāi)支滓走,特別是一個(gè)對(duì)象需要頻繁的創(chuàng)建垦江、銷毀時(shí),而且創(chuàng)建或銷毀時(shí)性能又無(wú)法優(yōu)化搅方,單例模式的優(yōu)勢(shì)就非常明顯比吭。
    • 單例模式可以避免對(duì)資源的多重占用,例如一個(gè)文件操作姨涡,由于只有一個(gè)實(shí)例存在內(nèi)存中衩藤,避免對(duì)同一資源文件的同時(shí)操作。
    • 單例模式可以在系統(tǒng)設(shè)置全局的訪問(wèn)點(diǎn)涛漂,優(yōu)化和共享資源訪問(wèn)赏表,例如检诗,可以設(shè)計(jì)一個(gè)單例類,負(fù)責(zé)所有數(shù)據(jù)表的映射處理瓢剿。
  • 缺點(diǎn):

    • 單例模式一般沒(méi)有接口逢慌,擴(kuò)展很困難,若要擴(kuò)展间狂,只能修改代碼來(lái)實(shí)現(xiàn)攻泼。
    • 單例對(duì)象如果持有Context,那么很容易引發(fā)內(nèi)存泄露鉴象。此時(shí)需要注意傳遞給單例對(duì)象的Context最好是Application Context忙菠。

更多內(nèi)容戳這里(整理好的各種文集)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纺弊,隨后出現(xiàn)的幾起案子牛欢,更是在濱河造成了極大的恐慌,老刑警劉巖俭尖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氢惋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡稽犁,警方通過(guò)查閱死者的電腦和手機(jī)焰望,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)已亥,“玉大人熊赖,你說(shuō)我怎么就攤上這事÷亲担” “怎么了震鹉?”我有些...
    開(kāi)封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)捆姜。 經(jīng)常有香客問(wèn)我传趾,道長(zhǎng),這世上最難降的妖魔是什么泥技? 我笑而不...
    開(kāi)封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任浆兰,我火速辦了婚禮,結(jié)果婚禮上珊豹,老公的妹妹穿的比我還像新娘簸呈。我一直安慰自己,他們只是感情好店茶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布蜕便。 她就那樣靜靜地躺著,像睡著了一般贩幻。 火紅的嫁衣襯著肌膚如雪轿腺。 梳的紋絲不亂的頭發(fā)上两嘴,一...
    開(kāi)封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音吃溅,去河邊找鬼溶诞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛决侈,可吹牛的內(nèi)容都是我干的螺垢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赖歌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枉圃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起庐冯,我...
    開(kāi)封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤孽亲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后展父,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體返劲,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年栖茉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了篮绿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吕漂,死狀恐怖亲配,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惶凝,我是刑警寧澤吼虎,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站苍鲜,受9級(jí)特大地震影響思灰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜混滔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一洒疚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遍坟,春花似錦拳亿、人聲如沸晴股。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)电湘。三九已至隔节,卻和暖如春鹅经,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怎诫。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工瘾晃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幻妓。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓蹦误,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肉津。 傳聞我的和親對(duì)象是個(gè)殘疾皇子强胰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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