1. 程式人生 > >最簡單的設計模式——單例模式

最簡單的設計模式——單例模式

single alt public 參考資料 mage 如何 數據開發 內存空間 同步代碼塊

前言

單例模式可以說是最簡單也是最常見的設計模式了,有些語言比如scala甚至在語言層面對其進行了支持。單例是指類的實例在全局只有一個。什麽時候我們希望類的實例在整個JVM進程中只有一個?比如說線程池:創建開銷很大;還有緩存:占用內存空間很多,而且超過一個也不利於維護。還有其他比如註冊表對象,日誌對象等等我們都希望它們全局唯一。單例模式指導我們如何創建這樣一個對象。

簡介

定義

確保一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。

結構

技術分享圖片

實現

單例的實現有很多種,按單例對象是否延遲初始化可以分為懶漢是和餓漢式。每一種的實現又有兩種變體。

常規餓漢式實現

public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {

        return INSTANCE;

    }

}

通過一個靜態變量引用單例實例,該變量在類加載的時候就會初始化,線程安全。

枚舉餓漢式實現

public enum  Singleton {
    INSTANCE
}

枚舉類型的本質如下,區別僅在於靜態變量是公有的,同樣也是線程安全。

public class Singleton extends Enum<Singleton> {
    public static final Singleton INSTANCE = new Singleton();
}

使用靜態內部類的懶漢式實現

public class Singleton {

    private Singleton() {

    }

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

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

}

只有在調用getInstance方法時才會加載靜態內部類SingletonHolder,該內有一個靜態成員變量,該成員變量在類加載的時候初始化,指向一個單例對象。同樣也線程安全。

帶雙重檢查的懶漢式實現

public class Singleton {

    private Singleton() {

    }

    private static volatile Singleton INSTANCE;

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

}

這是所有實現方式中最重要的。倒不是因為它平時用的多(大部分情況下直接用常規的餓漢式單例就行了),而是因為面試的時候經常會被問到,很多時候甚至會讓面試者把它現場手寫出來。因為其本身並不復雜,但能很好的考察對多線程的理解程度。而且以volitile,線程同步,類加載機制為入口可以深入考察多線程和jvm領域更深入的知識。比如:
1.volitile在這裏有什麽用?你知道它還有什麽功能嗎?它是否能保證線程安全?它的底層是怎麽實現的?

2.為什麽要在方法內加同步代碼塊?可以把同步關鍵字放在方法上嗎?它和前一種有什麽區別?你覺得哪個更好?

可惜我在上家公司用這個問題面了不少3年左右開發經驗求職者,能把這部分代碼大概寫對的都不到20%。可能因為我面的都是大數據開發工程師吧,java基礎會薄弱些。

參考資料
-《設計模式之禪》

最簡單的設計模式——單例模式