設(shè)計模式之單例模式

定義:確保一個類只有一個實例涵防,并提供對該實例的全局訪問,其構(gòu)造函數(shù)私有化吁讨。

單例模式的七種寫法

1、餓漢模式
public class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton(){
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}

這種方式在類加載時就完成了初始化峦朗,所以類加載較慢建丧,但獲取對象的速度快。這種方式基于類加載機制波势,避免了線程的同步問題翎朱。在類加載的時候就完成實例化,沒有達到懶加載的效果尺铣。如果從始至終未使用過這個實例拴曲,則會造成內(nèi)存的浪費。

2凛忿、懶漢模式
public class Singleton {
    private static Singleton instance;

    private Singleton(){
    }

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

這種懶漢模式聲明了一個靜態(tài)對象澈灼,在用戶第一次調(diào)用時初始化。這雖然節(jié)約了資源店溢,但第一次加載時需要實例化叁熔,反應(yīng)稍慢一些,主要是在多線程時不能正常工作床牧。

3荣回、懶漢模式(線程安全)
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方法時都需要進行同步心软,這會造成不必要的同步開銷革砸,而且大部分時候我們是用不到同步的,所以不建議用這種模式糯累。

4算利、雙重檢查模式(DCL)
public class Singleton {
    private static volatile Singleton instance;

    private Singleton(){
    }

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

這種寫法在getInstance方法中隊Singleton進行了兩次判空:第一次是為了不必要的同步,第二次是在Singleton等于null的情況下才創(chuàng)建實例泳姐。在這里使用volatile效拭,也會或多或少地影響性能,第一次加載時反應(yīng)稍慢胖秒,在高并發(fā)環(huán)境下也有一定的缺陷缎患。DCL雖然在一定程度上解決了資源的消耗和多余的同步、線程安全等問題阎肝,但其在某些情況下還是會出現(xiàn)失效的問題挤渔。
volatile作用:
(1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值风题,這新值對其他線程來說是立即可見的判导。
(2)禁止指令重排序優(yōu)化。(什么是指令重排序:是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元處理)沛硅。
DCL失效:
當一個A線程執(zhí)行到instance = new Singleton()語句時眼刃,這里看起來是一句代碼,實際上這不是一個原子操作摇肌,這句代碼最終會被編譯成多條匯編指令擂红,它大致做了3件事:
(1)給Singleton的實例分配內(nèi)存;
(2)調(diào)用Singleton()的構(gòu)造函數(shù)围小,初始化成員字段昵骤;
(3)將instance對象指向分配的內(nèi)存空間(此時instance就不是null了)
但是,由于Java編譯器允許處理器亂序執(zhí)行肯适,以及JDK1.5之前JMM(Java Memory Model变秦,即java內(nèi)存模型)中Cache、寄存器到主內(nèi)存回寫順序的規(guī)定疹娶,上面的第二和第三的順序是無法保證的伴栓。也就是說伦连,執(zhí)行順序可能是1-2-3,也可能是1-3-2雨饺。如果是后者,并且在3執(zhí)行完畢惑淳、2未執(zhí)行之前额港,被切換到線程B上,這時候instance因為已經(jīng)在線程A內(nèi)執(zhí)行過了第三點歧焦,instance已經(jīng)是非空了移斩,所以肚医,線程B直接取走instance,再使用時就會出錯向瓷,這就是DCL失效問題肠套。

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

    private Singleton() {
    }

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

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

第一次加載Singleton是并不會初始化INSTANCE猖任,只有第一次調(diào)用getInstance方法是虛擬機加載SingletonHolder并初始化INSTANCE你稚。這樣不僅能確保線程安全,也能保證Singleton類的唯一性朱躺,所以是非常推薦的一種單例寫法刁赖。

6、枚舉單例模式
public enum Singleton {

    INSTANCE;

    public void doSomeThing() {
    }
}

默認枚舉實例的創(chuàng)建時線程安全的长搀,并且在任何情況下都是單例宇弛。前面講過的機制單例模式,有一種情況會重寫創(chuàng)建對象源请,那就是反序列化:將一個單例實例對象寫到磁盤再讀取回來枪芒,從而獲得了一個實例。反序列化操作提供了readResolve方法谁尸,這個方法可以讓開發(fā)者控制對象的反序列化病苗。所以,如果要杜絕單例對象反序列化時重寫生成對象症汹,必須加入如下方法:

private Object readResolve() throws ObjectStreamException {
        return SingletonHolder.INSTANCE;
}

枚舉單例的優(yōu)點就是簡單硫朦,不過Android使用enum之后的dex大小增加很多,運行時還會產(chǎn)生額外的內(nèi)存占用背镇,其可讀性也不高咬展,因此官方強烈建議不要在Android程序里面使用到enum,枚舉單例缺點也很明顯瞒斩。

7破婆、容器實現(xiàn)單例模式
/**
 * 單例容器類
 */
public class SingletonManager {
    private SingletonManager() {
    }

    private static Map<String, Object> instanceMap = new HashMap<>();

    public static void registerInstance(String key, Object instance) {
        if (!instanceMap.containsKey(key)) {
            instanceMap.put(key, instance);
        }
    }

    public static Object getInstance(String key) {
        return instanceMap.get(key);
    }
}


/**
 * 單例模板
 */
public class SingletonPattern {
    SingletonPattern() {
    }

    public void doSomething() {
        Log.d("==", "doSomeing");
    }
}

// 具體用法
SingletonManager.registerInstance("SingletonPattern", new SingletonPattern());
SingletonPattern singletonPattern = (SingletonPattern) SingletonManager.getInstance("SingletonPattern");
singletonPattern.doSomething();

這種方式,根據(jù)key獲取對象對應(yīng)類型的對象胸囱,隱藏了具體實現(xiàn)祷舀,降低了耦合度。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烹笔,一起剝皮案震驚了整個濱河市裳扯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谤职,老刑警劉巖饰豺,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異允蜈,居然都是意外死亡冤吨,警方通過查閱死者的電腦和手機蒿柳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漩蟆,“玉大人垒探,你說我怎么就攤上這事〉±睿” “怎么了叛复?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扔仓。 經(jīng)常有香客問我褐奥,道長,這世上最難降的妖魔是什么翘簇? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任撬码,我火速辦了婚禮,結(jié)果婚禮上版保,老公的妹妹穿的比我還像新娘呜笑。我一直安慰自己,他們只是感情好彻犁,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布叫胁。 她就那樣靜靜地躺著,像睡著了一般汞幢。 火紅的嫁衣襯著肌膚如雪驼鹅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天森篷,我揣著相機與錄音输钩,去河邊找鬼。 笑死仲智,一個胖子當著我的面吹牛买乃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钓辆,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剪验,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了前联?” 一聲冷哼從身側(cè)響起功戚,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛀恩,沒想到半個月后疫铜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡双谆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年壳咕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顽馋。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谓厘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寸谜,到底是詐尸還是另有隱情竟稳,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布熊痴,位于F島的核電站他爸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏果善。R本人自食惡果不足惜诊笤,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巾陕。 院中可真熱鬧讨跟,春花似錦、人聲如沸鄙煤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梯刚。三九已至凉馆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亡资,已是汗流浹背句喜。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沟于,地道東北人咳胃。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像旷太,于是被迫代替她去往敵國和親展懈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

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

  • 前言 本文主要參考 那些年供璧,我們一起寫過的“單例模式”存崖。 何為單例模式? 顧名思義睡毒,單例模式就是保證一個類僅有一個...
    tandeneck閱讀 2,512評論 1 8
  • 一.什么是單例模式 單例模式的定義:確保一個類只有一個實例来惧,并提供一個訪問他的全局訪問點。單例模式是幾個設(shè)計模式中...
    Geeks_Liu閱讀 2,227評論 0 10
  • 概述 單例模式是應(yīng)用最廣的模式之一演顾,在應(yīng)用這個模式時供搀,單例對象的類必須保證只有一個實例存在隅居。許多時候整個系統(tǒng)只需要...
    劉滌生閱讀 1,022評論 0 5
  • 單例模式,顧名思義葛虐,指的是一個類只存在一個實例胎源。 那么,如何保證某一個類只存在一個實例呢屿脐?對象的創(chuàng)建是通過類的構(gòu)造...
    哇哇哇one閱讀 311評論 0 2
  • 今天下午去參加一位朋友公司項目說明會涕蚤,現(xiàn)在真的要出來學(xué)習(xí),世界變化太快的诵,微信代替短信万栅,天貓代替商場,還有的專用詞...
    步步嬌閱讀 86評論 0 0