詳解 - 單例模式

Android設(shè)計(jì)模式”這個(gè)系列主要是對Android項(xiàng)目中的設(shè)計(jì)模式進(jìn)行分析總結(jié),學(xué)習(xí)自《Android 源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》,錯(cuò)誤之處煩請指正~


Android設(shè)計(jì)模式系列文章:

1、詳解 - 單例模式
2宪赶、詳解 - Builder模式


一宗弯、 概述

1.1 定義

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

1.2 使用場景

確保某個(gè)類有且只有一個(gè)對象的場景蒙保,避免產(chǎn)生多個(gè)對象消耗過多的資源;或者某種類型的對象應(yīng)該有且只有一個(gè)欲主。

eg:創(chuàng)建一個(gè)對象需要消耗的資源過多追他,如訪問IO和數(shù)據(jù)庫資源。

1.3 關(guān)鍵點(diǎn)

  • 構(gòu)造函數(shù)不對外開放岛蚤,一般為 private 邑狸;
  • 通過一個(gè)靜態(tài)方法或者枚舉返回單例類對象;
  • 確保單例類的對象有且只有一個(gè)涤妒,尤其是在 多線程 環(huán)境下单雾;
  • 確保單例類對象在反序列化時(shí)不會重新構(gòu)建對象。

二她紫、實(shí)現(xiàn)方式

2.1 懶漢模式

聲明一個(gè)靜態(tài)對象硅堆,并且在用戶第一次調(diào)用 getInstance 時(shí)進(jìn)行初始化。

2.1.1 分析

  • synchronized 關(guān)鍵字用于在多線程情況下保證單例對象唯一性

  • 優(yōu)點(diǎn):單例只有在使用時(shí)才會被實(shí)例化贿讹,在一定程度上節(jié)約了資源

  • 缺點(diǎn):

    • 每一次加載時(shí)需要及時(shí)進(jìn)行實(shí)例化渐逃,響應(yīng)速度稍慢
    • 每次調(diào)用 getInstance() 都進(jìn)行同步,造成不必要的同步開銷
  • 一般不建議使用

2.1.2 源碼

public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        if (null == instance) {
            instance = new Singleton();//加載時(shí)進(jìn)行實(shí)例化
        }
        return instance;
    }
}

2.2 餓漢模式

聲明靜態(tài)對象時(shí)就已經(jīng)初始化民褂。

2.2.1 分析

  • 靜態(tài)對象在聲明的時(shí)候就已經(jīng)初始化茄菊,從而保證了單例對象唯一性

  • 優(yōu)點(diǎn): 每次調(diào)用 getInstance() 直接取出靜態(tài)對象疯潭,不需要同步鎖,響應(yīng)速度快

  • 缺點(diǎn):初始化聲明對象造成了一定資源的閑置浪費(fèi)

2.2.2 源碼

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

    private Singleton() {

    }

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

2.3 Double Check Lock (DCL) 模式

2.3.1 分析

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

    • 資源利用率高
    • 既能夠在需要時(shí)才初始化單例面殖,又能夠保證線程安全竖哩,且單例對象初始化后調(diào)用 getInstance() 不進(jìn)行同步鎖
  • 缺點(diǎn):

    • 第一次加載時(shí)響應(yīng)稍慢
    • 由于Java內(nèi)存模型的原因偶爾會失敗
      • instance = new Singleton(); 這句代碼并不是一個(gè)原子操作,由于 Java 編譯器允許處理器亂序執(zhí)行匯編指令以及 JDK1.5 之前的 JVM (Java Memory Model, Java 內(nèi)存模型) 中Cache脊僚、寄存器到主內(nèi)存回寫順序的規(guī)定相叁,該語句轉(zhuǎn)換的匯編指令無法確保順序執(zhí)行
      • JDK1.5 之后,具體化了 volatile 關(guān)鍵字辽幌,因此可以直接定義成 private volatile static Singleton instance = null; 增淹,就可以保證 instance 對象每次都是從主內(nèi)存中讀取

2.3.2 源碼

public class Singleton {
    private volatile static Singleton instance = null;

    private Singleton() {

    }

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

2.4 靜態(tài)內(nèi)部類單例模式

2.4.1 分析

強(qiáng)烈推薦使用

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

    • 第一次加載 Singleton 類時(shí)并不會初始化 instance ,只有在第一次調(diào)用 getInstance() 時(shí)才會初始化
    • 既能保證線程安全乌企,也能保證單例對象的唯一性埠通,同時(shí)也延遲了單例的實(shí)例化

2.4.2 源碼

public class Singleton {
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

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

2.5 枚舉單例

2.5.1 分析

枚舉單例模式最大的優(yōu)點(diǎn)是寫法簡單,枚舉在 Java 中與普通類是一樣的逛犹,不僅能夠有字段端辱,還能夠有自己的方法。最重要的是默認(rèn)枚舉實(shí)例的創(chuàng)建時(shí)線程安全的虽画,并且在任何情況下它都是一個(gè)單例舞蔽。

在上述的幾種單例模式中,反序列化 的時(shí)候會出現(xiàn)重新創(chuàng)建對象的情況码撰。**

上述示例中如果要杜絕單利對象在被反序列化時(shí)重新生成對象渗柿,則必須加入如下方法:

private Object readResolve() throws ObjectStreamException {
        return instance;
}

2.5.2 源碼

public enum  Singleton {
    
    INSTANCE;
    
    public void doSomething() {
        // ... do something
    }
    
}

2.6 使用容器實(shí)現(xiàn)單例模式

2.6.1 分析

在程序初始化的時(shí)候,將多種單例類型注入到一個(gè)統(tǒng)一的管理類中脖岛,在使用時(shí)根據(jù) key 獲取對象對應(yīng)類型的對象朵栖。

這種方式使得我們可以管理多種類型的單例,并且在使用時(shí)候可以通過統(tǒng)一的接口進(jìn)行獲取操作柴梆,降低了用戶的使用成本陨溅,也對用戶隱藏了具體實(shí)現(xiàn),降低了耦合度绍在。

2.6.2 源碼

public class SingletonManager {
    private static Map<String, Object> data = new HashMap<>();

    public SingletonManager() {
    }
    
    public static void register(String key, Object instance) {
        if (!data.containsKey(key)) {
            data.put(key, instance);
        }
    }
    
    public static Object get(String key) {
        return data.get(key);
    }
}

三门扇、小結(jié)

所有的單例模式核心原理都是將構(gòu)造函數(shù)私有化,并且通過靜態(tài)方法獲取一個(gè)唯一的實(shí)例偿渡。

需要注意的是在獲取實(shí)例的過程中保證線程安全臼寄、防止反序列化導(dǎo)致重新生成實(shí)例對象等問題。

具體選擇哪種方式實(shí)現(xiàn)單例模式還需要結(jié)合項(xiàng)目業(yè)務(wù)邏輯溜宽。


本文對 單例模式 的分析到此就結(jié)束了吉拳,部分內(nèi)容學(xué)習(xí)自 《Android源碼設(shè)計(jì)模式 解析與實(shí)戰(zhàn)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末适揉,一起剝皮案震驚了整個(gè)濱河市留攒,隨后出現(xiàn)的幾起案子煤惩,更是在濱河造成了極大的恐慌,老刑警劉巖稼跳,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盟庞,死亡現(xiàn)場離奇詭異吃沪,居然都是意外死亡汤善,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門票彪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來红淡,“玉大人,你說我怎么就攤上這事降铸≡诤担” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵推掸,是天一觀的道長桶蝎。 經(jīng)常有香客問我,道長谅畅,這世上最難降的妖魔是什么登渣? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮毡泻,結(jié)果婚禮上胜茧,老公的妹妹穿的比我還像新娘。我一直安慰自己仇味,他們只是感情好呻顽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丹墨,像睡著了一般廊遍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贩挣,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天昧碉,我揣著相機(jī)與錄音,去河邊找鬼揽惹。 笑死被饿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的搪搏。 我是一名探鬼主播狭握,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疯溺!你這毒婦竟也來了论颅?” 一聲冷哼從身側(cè)響起哎垦,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恃疯,沒想到半個(gè)月后漏设,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡今妄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年郑口,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盾鳞。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犬性,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腾仅,到底是詐尸還是另有隱情乒裆,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布推励,位于F島的核電站鹤耍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏验辞。R本人自食惡果不足惜稿黄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望受神。 院中可真熱鬧抛猖,春花似錦、人聲如沸鼻听。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撑碴。三九已至撑教,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間醉拓,已是汗流浹背伟姐。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亿卤,地道東北人愤兵。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像排吴,于是被迫代替她去往敵國和親秆乳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

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