設(shè)計(jì)模式:?jiǎn)卫J?/h1>

摘要:常規(guī)設(shè)計(jì)模式之單例模式

前言

設(shè)計(jì)模式作為我們開(kāi)發(fā)人員必不可少的編程思想,我們有必要好好的去學(xué)習(xí)虚青、理解和掌握它呀。今天開(kāi)始我會(huì)整理自己所有學(xué)習(xí)過(guò)和沒(méi)有學(xué)習(xí)過(guò)的設(shè)計(jì)模式纵穿,希望大家一起多多交流。

這一章主要介紹我們常用的單例模式奢人,它解決了我們需要反復(fù)獲取那些占用較大資源和開(kāi)銷(xiāo)的對(duì)象的問(wèn)題。并且單例出來(lái)的對(duì)象往往持有我們需要的一些狀態(tài)供我們使用何乎。

單例分類(lèi)

1. 餓漢式

餓漢式單例模式是基于classloder機(jī)制避免了多線程的同步問(wèn)題。但是支救,instance在類(lèi)裝載時(shí)就會(huì)實(shí)例化,這時(shí)候初始化instance是沒(méi)有達(dá)到lazy loading的效果的各墨。

public final class HungrySingleton {

    /* 還有一種方式是在靜態(tài)代碼塊中進(jìn)行demo的初始化,但是在
        多線程操作時(shí)欲主,會(huì)出現(xiàn)空引用問(wèn)題。
    */
    private static final HungrySingleton demo = new HungrySingleton();
    
    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return demo;
    }
}

2. 懶漢式

  • 線程不安全的懶漢式扁瓢,實(shí)例化方法在多線程訪問(wèn)時(shí)可能出現(xiàn)多實(shí)例详恼,測(cè)試時(shí)出現(xiàn)多實(shí)例的概率雖然小昧互,但是線程不安全
public final class LazyLoadingUnSafety {

    private static LazyLoadingUnSafety lazyLoading;

    private LazyLoadingUnSafety(){}

    public static LazyLoadingUnSafety getInstance(){
        if (lazyLoading == null){
            lazyLoading = new LazyLoadingUnSafety();
        }
        return lazyLoading;
    }
}
  • 線程安全的懶漢式
public final class LazyLoadingSafe {

    private static LazyLoadingSafe lazyLoading;

    private LazyLoadingSafe(){
        // 避免通過(guò)反射進(jìn)行實(shí)例的初始化
        if (lazyLoading == null) {
            lazyLoading = this;
        } else {
            throw new IllegalStateException("Already initialized.");
        }
    }

    /**
     * 通過(guò)synchronized 關(guān)鍵字進(jìn)行實(shí)例化方法的線程安全
     * @return LazyLoadingSafe 返回的單實(shí)例
     */
    public static synchronized LazyLoadingSafe getInstance(){
        if (lazyLoading == null){
            lazyLoading = new LazyLoadingSafe();
        }
        return lazyLoading;
    }

}

3. 靜態(tài)內(nèi)部類(lèi)

利用了classloder的機(jī)制來(lái)保證初始化instance時(shí)只有一個(gè)線程挽铁,虛擬機(jī)會(huì)保證一個(gè)類(lèi)的類(lèi)構(gòu)造器clinit()在多線程環(huán)境中被正確的加鎖叽掘、同步,如果多個(gè)線程同時(shí)去初始化一個(gè)類(lèi)玖雁,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類(lèi)的類(lèi)構(gòu)造器clinit(),其他線程都需要阻塞等待赫冬,直到活動(dòng)線程執(zhí)行clinit()方法完畢。

public final class InnerClassSingleton {

    private InnerClassSingleton(){}

    public static InnerClassSingleton getInstance(){
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        public static final InnerClassSingleton instance = new InnerClassSingleton();
    }
}

4. 枚舉單例

枚舉形式單例劲厌,解決多線程和能防止反序列化重新創(chuàng)建新的對(duì)象的單例模式, 此方式是線程安全的,但是添加其他方法就需要開(kāi)發(fā)者去自己保證方法的線程安全补鼻。

public class EnumSingleton {

    private int tickets = 1000;

    private EnumSingleton(){}

    public static EnumSingleton getInstance(){
        return Singleton.SINGLETON.getInstance();
    }

    // 線程不安全
    public int getTickets(){
        return tickets++;
    }
    
    enum Singleton {

        SINGLETON;

        private EnumSingleton enumSingleton;

        Singleton() {
            this.enumSingleton = new EnumSingleton();
        }

        private EnumSingleton getInstance(){
            return this.enumSingleton;
        }
    }

5. 雙重檢索單例

通過(guò)在實(shí)例化方法中增加鎖進(jìn)行線程安全保護(hù),而實(shí)例變量singleton需要通過(guò)volatile關(guān)鍵字防止實(shí)例化方法在執(zhí)行時(shí)進(jìn)行指令重排出現(xiàn)線程安全問(wèn)題风范。

public final class DoubleLockSingleton {

    // 不使用volatile 可能發(fā)生指令重排導(dǎo)致socket沒(méi)有被初始化完畢報(bào)空指針異常
    private static volatile DoubleLockSingleton singleton;

    private Socket socket;

    private DoubleLockSingleton(){

        // 此處阻止通過(guò)反射實(shí)例化實(shí)例
        if (singleton != null) {
            throw new IllegalStateException("Already initialized.");
        }
        this.socket = new Socket();
    }

    public static DoubleLockSingleton getInstance(){

        // 避免每次進(jìn)入都需要進(jìn)入同步代碼塊,提高效率
        if (singleton == null){
            synchronized(DoubleLockSingleton.class){
                if (singleton == null) {
                    singleton = new DoubleLockSingleton();
                }
            }
        }
        return singleton;
    }
}

6. CAS 線程安全單例

最新學(xué)習(xí)的單例實(shí)現(xiàn)方式乌企,主要通過(guò)java提供的cas機(jī)制保證去實(shí)例化對(duì)象的時(shí)候?yàn)樽钚聦?duì)象成玫。

public class CASSingleton {

    private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<>();

    public CASSingleton() {
    }

    public static final CASSingleton getInstance() {
        for (;;) {
            CASSingleton currentInstance = INSTANCE.get();

            if (currentInstance != null) {
                return currentInstance;
            }

            currentInstance = new CASSingleton();

            if (INSTANCE.compareAndSet(null, currentInstance)) {
                return currentInstance;
            }
        }
    }

總結(jié)

單例模式在我們?nèi)粘i_(kāi)發(fā)代碼中會(huì)大量使用,即使沒(méi)有自己經(jīng)常去創(chuàng)建單例哭当,但在我們所使用的各種框架中被大量,比如spring等钦勘。這種模式值得我們好好去總結(jié)與學(xué)習(xí)。

還看到一篇文章使用ThreadLocal進(jìn)行單例模式的設(shè)計(jì)彻采,但是在我目前的知識(shí)體系中腐缤,ThreadLocal為每個(gè)線程提供變量的一個(gè)副本肛响,也就是我們存到ThreadLocal中的對(duì)象會(huì)以副本的形式被每個(gè)線程使用,最終的測(cè)試結(jié)果是不同線程使用的對(duì)象是不一致的特笋,我個(gè)人認(rèn)為這種單例she設(shè)計(jì)不太合理,各位大佬可以提提意見(jiàn)。

public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> local = ThreadLocal.withInitial(() -> new ThreadLocalSingleton());

    private ThreadLocalSingleton(){
    }

    public static ThreadLocalSingleton getInstance(){
        return local.get();
    }
}

Github地址:詳細(xì)代碼可以參考我的GitHub虎囚,謝謝大佬指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市淘讥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌适揉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫉嘀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡剪侮,警方通過(guò)查閱死者的電腦和手機(jī)拭宁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)杰标,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人彩匕,你說(shuō)我怎么就攤上這事⊥找牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵绪爸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我奠货,道長(zhǎng)介褥,這世上最難降的妖魔是什么递惋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮萍虽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贩挣。我一直安慰自己没酣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布裕便。 她就那樣靜靜地躺著,像睡著了一般见咒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上改览,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音宝当,去河邊找鬼视事。 笑死庆揩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的订晌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼锈拨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奕枢?” 一聲冷哼從身側(cè)響起娄昆,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤验辞,失蹤者是張志新(化名)和其女友劉穎喊衫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體族购,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年寝杖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑟幕。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡留潦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辣往,到底是詐尸還是另有隱情,我是刑警寧澤站削,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站许起,受9級(jí)特大地震影響十偶,放射性物質(zhì)發(fā)生泄漏园细。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一珊肃、第九天 我趴在偏房一處隱蔽的房頂上張望荣刑。 院中可真熱鬧伦乔,春花似錦、人聲如沸烈和。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恬试。三九已至疯暑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妇拯,已是汗流浹背幻馁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工越锈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甘凭。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丹弱,于是被迫代替她去往敵國(guó)和親德撬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355