設計模式之 - 單例模式

1. 預熱

爲了小夥伴們能夠比較好的理解後面講解,先將一些前置的概念在這裏進行預熱,假如你已經知道這裏概念,你可以跳過這個小結。

  • 指令序重排:Java內存模型中,允許編譯器和處理器對指令進行重排序,重排序過程不會影響到單線程程序的執行,會影響到多線程併發執行的正確性
  • volatile 可見性生命:通過加入內存屏障禁止重排序優化來實現
2. 實現方法分析

實現單例模式估計是全地球人都知道的設計模式了,它又有懶漢模式餓漢模式之分,區別在於對單例對象初始化的時機不同,懶漢模式顧名思義,比較懶,所以對象的初始化會放到使用的那一個刻纔會進行!餓漢反之,具體孰好孰壞,這個得兩說,就餓漢來說,如果單例對象比較輕(廢話,要是很輕還用單例搞鬼麼)和初始化比較快且後續一定使用的話,這個就無所謂了!比較推薦,但是如果不用,這樣就有點浪費服務器的性能和資源了!接下來我會提供這兩種模式的幾種實現方式!

a) 懶漢模式

/**
 * 兩種經典的餓漢式實現
 *
 * @author Lin.Jianfei
 * @version 1.0.0
 * @since 2018-05-07
 */
class Singleton4Eager1 {

    private Singleton4Eager1() {}

    private static Singleton4Eager1 INSTANCE = new Singleton4Eager1();

    public static Singleton4Eager1 getInstance() {
        return INSTANCE;
    }
}

class Singleton4Eager2 {
    private Singleton4Eager2() {}

    // 需要注意靜態代碼的順序,靜態代碼塊是按照編寫的順序執行
    private static Singleton4Eager2 INSTANCE = null;

    static {
        INSTANCE = new Singleton4Eager2();
    }

    public static Singleton4Eager2 getInstance() {
        return INSTANCE;
    }
}

b) 懶漢式

class Singleton4Lazy1 {
    private Singleton4Lazy1() {}

    private Singleton4Lazy1 instance = null;

    // 線程不安全
    public Singleton4Lazy1 getInstance() {
        if (instance == null) {
            instance = new Singleton4Lazy1();
        }

        return instance;
    }
}

class Singleton4Lazy2 {
    private Singleton4Lazy2() {}

    private Singleton4Lazy2 instance = null;

    // 線程安全,效率低下
    public synchronized Singleton4Lazy2 getInstance() {
        if (instance == null) {
            instance = new Singleton4Lazy2();
        }

        return instance;
    }
}

class Singleton4Lazy3 {
    private Singleton4Lazy3() {}

    // 這裏加上 volatile 修飾,禁止指令優化重排
    private volatile Singleton4Lazy3 instance = null;

    // 線程安全,引入 volatile + 雙重校驗 改善併發性能,
    public synchronized Singleton4Lazy3 getInstance() {
        if (instance == null) {
            synchronized (Singleton4Lazy3.class) {
                if (instance == null) {
                    instance = new Singleton4Lazy3();
                }
            }
        }

        return instance;
    }
}

class Singleton4Lazy4 {
    private Singleton4Lazy4() {}

    private Singleton4Lazy4 instance = null;

    public Singleton4Lazy4 getInstance() {
        return InnerSingleton.INSTANCE.getInstance();
    }

    private enum InnerSingleton {
        INSTANCE;

        private Singleton4Lazy4 instance;

        // 利用虛擬機特性,保證延遲且只執行一次創建,避免的使用鎖,性能最好
        InnerSingleton() {
            this.instance = new Singleton4Lazy4();
        }

        public Singleton4Lazy4 getInstance() {
            return this.instance;
        }
    }
}
3. 總結

這裏,實現餓漢式有好多種方式可以實現,可以根據自己的業務場景需要進行選擇,如果是單線程的場景,無疑是第一種最好,類似StringBuilder 和 StringBuffer。假如涉及到併發的話,建議採用最後用枚舉方式的實現,即避免了鎖的損耗且延遲加載!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章