首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深度解析 Java 單例模式:從懶漢、餓漢到靜態內部類與枚舉的最佳實踐

     編輯:Java 相關知識

前言

單例模式確保類在全局僅有一個實例,並提供訪問點。Java 中實現單例有多種寫法,各有優劣。本文系統梳理 5 種主流實現,重點分析線程安全、延遲加載、序列化安全、反射攻擊防禦,推薦生產環境最佳選擇。

五種實現方式對比

1. 餓漢式

public class HungrySingleton {
    private static final HungrySingleton INSTANCE = new HungrySingleton();
    private HungrySingleton() {}
    public static HungrySingleton getInstance() { return INSTANCE; }
}
  • 優點:類加載時初始化,JVM 保證線程安全,代碼最簡潔。
  • 缺點:無延遲加載,若實例佔用大量資源且不一定使用,造成浪費。

2. 懶漢式(同步鎖)

public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton() {}
    public static synchronized LazySingleton getInstance() {
        if (instance == null) instance = new LazySingleton();
        return instance;
    }
}
  • 優點:延遲加載。
  • 缺點:synchronized 鎖定整個方法,高併發下效能極差(實際只需首次加鎖)。

3. 雙重檢查鎖

public class DclSingleton {
    private static volatile DclSingleton instance;
    private DclSingleton() {}
    public static DclSingleton getInstance() {
        if (instance == null) {
            synchronized (DclSingleton.class) {
                if (instance == null) instance = new DclSingleton();
            }
        }
        return instance;
    }
}
  • 關鍵:volatile 禁止指令重排,保證構造函數執行完成前不暴露引用。
  • 優點:延遲加載、高併發效能好。
  • 缺點:代碼較複雜,易寫錯(漏 volatile、判斷順序錯誤)。

4. 靜態內部類(推薦)

public class HolderSingleton {
    private HolderSingleton() {}
    private static class Holder {
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    public static HolderSingleton getInstance() { return Holder.INSTANCE; }
}
  • 原理:利用類加載機制,外部類加載時不初始化內部類,首次調用 getInstance() 觸發內部類加載並初始化 INSTANCE,JVM 保證線程安全。
  • 優點:延遲加載、線程安全、代碼簡潔、無鎖開銷、防反射攻擊(構造器私有)。
  • 推薦:生產環境首選。

5. 枚舉單例(最強)

public enum EnumSingleton {
    INSTANCE;
    public void doSomething() { /* ... */ }
}
  • 優點:語法最簡潔、天然線程安全、防序列化破壞(JVM 保證枚舉常量唯一)、防反射攻擊(反射無法實例化枚舉)。
  • 缺點:無延遲加載(類加載時初始化)、無法繼承、較難擴展(如需實現介面可 implements)。
  • 推薦:追求極致安全、無延遲加載需求時首選。

進階話題:破壞單例與防禦

攻擊方式餓漢/靜態內部類雙重檢查鎖枚舉
反射調用私有構造器可破壞(需加入構造器檢查)可破壞免疫 (JVM 禁止)
序列化/反序列化可破壞(需實現 readResolve)可破壞免疫
類加載器隔離可產生多實例可產生多實例可產生多實例

防反射構造器範例

private Singleton() {
    if (Holder.INSTANCE != null) { // 或針對各自實例變量
        throw new IllegalStateException("單例已存在,禁止反射創建");
    }
}

防序列化破壞

// 非枚舉類需實現
private Object readResolve() { return INSTANCE; }

選型決策樹

  • 需延遲加載 + 簡潔 + 線程安全 → 靜態內部類
  • 需防序列化/反射破壞 + 簡潔 → 枚舉
  • 極簡場景、實例輕量、確定會用 → 餓漢式
  • 需手動控制初始化時機(如依賴配置文件) → 雙重檢查鎖 或手動調用初始化方法。

總結

單例模式看似簡單,實則蘊含 Java 記憶體模型、類加載機制、反射/序列化機制等核心知識。靜態內部類 平衡了延遲加載、線程安全、代碼簡潔,是最通用的推薦寫法;枚舉 則是安全性最高的終極形態。理解各實現的底層原理與適用邊界,才能在架構設計中做出正確選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved