目錄
單例模式可以說是最簡單也是最常見的設計模式了,有些語言比如scala甚至在語言層面對其進行了支持。單例是指類的實例在全局只有一個。什么時候我們希望類的實例在整個JVM進程中只有一個?比如說線程池:創建開銷很大;還有緩存:占用內存空間很多,而且超過一個也不利於維護。還有其他比如注冊表對象,日志對象等等我們都希望它們全局唯一。單例模式指導我們如何創建這樣一個對象。
2. 單例模式詳解
2.1 單例模式定義
確保一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。
2.2 單例模式類結構

2.3 單例模式實現
單例的實現有很多種,按單例對象是否延遲初始化可以分為懶漢是和餓漢式。每一種的實現又有兩種變體。
2.3.1常規餓漢式實現
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
通過一個靜態變量引用單例實例,該變量在類加載的時候就會初始化,線程安全。
2.3.2 枚舉餓漢式實現
public enum Singleton {
INSTANCE
}
枚舉類型的本質如下,區別僅在於靜態變量是公有的,同樣也是線程安全。
public class Singleton extends Enum<Singleton> {
public static final Singleton INSTANCE = new Singleton();
}
2.3.3 使用靜態內部類的懶漢式實現
public class Singleton {
private Singleton() {
}
static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
只有在調用getInstance方法時才會加載靜態內部類SingletonHolder,該內有一個靜態成員變量,該成員變量在類加載的時候初始化,指向一個單例對象。同樣也線程安全。
2.3.4 帶雙重檢查的懶漢式實現
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基礎會薄弱些。
參考資料
-《設計模式之禪》
