設(shè)計模式系列教程—Singleton Pattern(單例模式)

前言:單例模式是為了解決在程序中只能有一個的問題西剥,例如在我們的程序中經(jīng)常用到的線程池、緩存辛润、對話框和注冊表等對象永淌,都只需要實例化一個崎场,后面其他線程要用的時候都直接拿過來用即可。

案例分析:

REQ1:Vander接到這么個需求遂蛀,就是要創(chuàng)建一個糖果工廠來制造糖果谭跨,在糖果工廠放入蔗糖原料前必須保證熔爐是空的,接著在熔爐中煮沸并加入其它原料李滴,此時必須保證熔爐不是空的并且是沒有煮過的螃宙,最后將熔爐中的糖漿倒出到下一個機(jī)器進(jìn)行冷卻再加工。設(shè)計如下:

image.png

分析:首先這需要保證所坯,程序中有且僅有一個CandyBoiler谆扎,不然就可能會出現(xiàn)在沒有fill之前就直接boil了的情況,首先要保證能夠?qū)嵗荒芊旁陬愅饷媲壑钥梢酝ㄟ^將構(gòu)造方法私有化堂湖,于是代碼就是這么寫的:

public class CandyBoiler1 {

    private boolean empty;
    
    private boolean boiled;
    
    private static CandyBoiler1 candyBoiler1;
    
    private CandyBoiler1() {
        System.out.println("_______________CandyBoiler Constructor____________");
        empty = true;
        boiled = false;
    }

    public static CandyBoiler1 getInstance() {
        if(candyBoiler1 == null) {
            candyBoiler1 = new CandyBoiler1(); 
        }
        return candyBoiler1;
    }
    
    public void fill() {
        if(isEmpty()) {
            System.out.println("___________fill material___________");
            empty = false;
            boiled = false;
        }
    }
    
    public void boil() {
        if((!isBoiled()) && (!isEmpty())) {
            System.out.println("___________boil material___________");
            empty = false;
            boiled = true;
        }
    }
    
    public void drain() {
        if((isBoiled()) && (!isEmpty())) {
            System.out.println("___________drain material___________");
            //將糖漿倒出后恢復(fù)原狀態(tài)
            empty = true;
            boiled = false;
        }
    }
    
    public void makeCandy() {
        fill();
        boil();
        drain();
    }
    
    public boolean isEmpty() {
        return empty;
    }

    public boolean isBoiled() {
        return boiled;
    }
    
}

說明:這么一來闲先,在實現(xiàn)的時候發(fā)現(xiàn),無法應(yīng)對多線程的情況:

public class Main {

    public static void main(String[] args) {
        CandyProducer1 pro1 = new CandyProducer1();
        Thread thread1 = new Thread(pro1);
        CandyProducer2 pro2 = new CandyProducer2();
        Thread thread2 = new Thread(pro2);
        CandyProducer3 pro3 = new CandyProducer3();
        Thread thread3 = new Thread(pro3);
        
        thread1.start();
        thread2.start();
        thread3.start();

    }

}

結(jié)果:發(fā)現(xiàn)CandyBoiler被創(chuàng)建了3次无蜂,為什么呢伺糠?


image.png

說明:這是由于線程1剛開始肯定需要創(chuàng)建新的CandyBoiler,接著進(jìn)入構(gòu)造方法中斥季,打印CandyBoiler Constructor训桶,打印較為費時,此時線程2來了酣倾,線程2發(fā)現(xiàn)此時candyBoiler1為null舵揭,因為實際上此時線程1還在處理print語句,還沒有完成創(chuàng)建呢躁锡,這樣就導(dǎo)致了創(chuàng)建了兩個CandyBoiler午绳。


image.png
解決方法1:synchronized

Vander 又開始改進(jìn)設(shè)計了,Vander想起以前學(xué)過synchronized關(guān)鍵字稚铣,這樣還不簡單箱叁,每次只允許一個線程訪問getInstance方法,在getInstance方法加入synchronized關(guān)鍵字即可惕医。
重新修改getInstance方法:

    public static synchronized CandyBoiler2 getInstance() {
        if(candyBoiler2 == null) {
            candyBoiler2 = new CandyBoiler2(); 
        }
        return candyBoiler2;
}

REQ2:接著Vander進(jìn)行測試,果然只創(chuàng)建了一個CandyBoiler對象算色,正當(dāng)Vander沾沾自喜抬伺,以為完美解決了問題的時候,Panda出現(xiàn)了灾梦,Panda說你這么寫你就不怕影響性能嗎峡钓?每次線程要來獲取CandyBoiler的時候,都需要等其它線程拿完了才能拿若河,這樣根本無法應(yīng)對高并發(fā)量的需求能岩。Vander一聽,難道還有別的解決方法嗎萧福?

解決方法2:使用“急切”創(chuàng)建實例**

Panda大師說這個問題其實還有兩種解決方法拉鹃,其實new CandyBoiler(),這句話只會運行一次鲫忍,其實也可以在剛開始加載CandyBoiler的時候膏燕,直接就把這個鍋爐對象new出來,后面全是拿來用就行了悟民。

public class CandyBoiler3 {

    private boolean empty;
    
    private boolean boiled;
    
    private static CandyBoiler3 candyBoiler3 = new CandyBoiler3();
    
    private CandyBoiler3() {
        System.out.println("_______________CandyBoiler Constructor____________");
        empty = true;
        boiled = false;
    }

    public static CandyBoiler3 getInstance() {
        return candyBoiler3;
    }
    … … … …
    … … … …
}

說明:壞處:提前將實例創(chuàng)建好坝辫,而不是等到要用的時候才創(chuàng)建,如果應(yīng)用程序總是創(chuàng)建并使用單例射亏,或者在創(chuàng)建和運行時方面的負(fù)擔(dān)不太繁重近忙,可以在初始化這個類的時候就創(chuàng)建此單件竭业。

解決方法3:**使用“雙重檢查加鎖”,在getInstance()中減少使用同步及舍。

利用雙重檢查加鎖永品,首先檢查是否實例已經(jīng)創(chuàng)建,如果尚未創(chuàng)建才進(jìn)行同步击纬,這樣依賴就只有第一次會同步鼎姐。

public class CandyBoiler4 {

    private boolean empty;
    
    private boolean boiled;
    
    private volatile static CandyBoiler4 candyBoiler4;
    
private CandyBoiler4() {
        System.out.println("_______________new CandyBoiler()____________");
        empty = true;
        boiled = false;
    }

    public static CandyBoiler4 getInstance() {
        if(candyBoiler4 == null) {
            synchronized (CandyBoiler4.class) {
                if(candyBoiler4 == null) {
                    candyBoiler4 = new CandyBoiler4();  
                }
            }
        }
        return candyBoiler4;
    }
    … … … …
    … … … …

}

最后的最后,我們又來總結(jié)我們現(xiàn)在現(xiàn)有的設(shè)計模式武器更振。

面向?qū)ο蠡A(chǔ)

抽象炕桨、封裝、多態(tài)肯腕、繼承

六大設(shè)計原則

設(shè)計原則一:封裝變化
設(shè)計原則二:針對接口編程献宫,不針對實現(xiàn)編程。
設(shè)計原則三:多用組合实撒,少用繼承姊途。
設(shè)計原則四:為交互對象之間的松耦合設(shè)計而努力
設(shè)計原則五:對擴(kuò)展開放,對修改關(guān)閉
設(shè)計原則六:依賴抽象知态,不要依賴于具體的類

模式

單例模式:確保一個類只有一個實例捷兰,并提供全局訪問點。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末负敏,一起剝皮案震驚了整個濱河市贡茅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌其做,老刑警劉巖顶考,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異妖泄,居然都是意外死亡驹沿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蹈胡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渊季,“玉大人,你說我怎么就攤上這事审残∷笥颍” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵搅轿,是天一觀的道長病涨。 經(jīng)常有香客問我,道長璧坟,這世上最難降的妖魔是什么既穆? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任赎懦,我火速辦了婚禮,結(jié)果婚禮上幻工,老公的妹妹穿的比我還像新娘励两。我一直安慰自己,他們只是感情好囊颅,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布当悔。 她就那樣靜靜地躺著,像睡著了一般踢代。 火紅的嫁衣襯著肌膚如雪盲憎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天胳挎,我揣著相機(jī)與錄音饼疙,去河邊找鬼。 笑死慕爬,一個胖子當(dāng)著我的面吹牛窑眯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播医窿,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼磅甩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了留搔?” 一聲冷哼從身側(cè)響起更胖,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隔显,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饵逐,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡括眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了倍权。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掷豺。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖薄声,靈堂內(nèi)的尸體忽然破棺而出当船,到底是詐尸還是另有隱情,我是刑警寧澤默辨,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布德频,位于F島的核電站,受9級特大地震影響缩幸,放射性物質(zhì)發(fā)生泄漏壹置。R本人自食惡果不足惜竞思,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钞护。 院中可真熱鬧盖喷,春花似錦、人聲如沸难咕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽余佃。三九已至暮刃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咙冗,已是汗流浹背沾歪。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留雾消,地道東北人灾搏。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像立润,于是被迫代替她去往敵國和親狂窑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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