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

Java 單例模式五種寫法深度解析與線程安全對比

     編輯:Java 相關知識

單例模式確保類在全局僅有一個實例,並提供訪問入口。本文系統對比五種主流實現:餓漢式、懶漢式同步、雙重檢查鎖 (DCL)、靜態內部類、枚舉,分析線程安全性、延遲加載、序列化/反射破壞防禦與性能,給出生產環境選型建議。

單例模式核心要求

  1. 構造器私有化,防止外部 `new`
  2. 提供靜態工廢方法獲取唯一實例
  3. 線程安全:多線程並發首次獲取時不創建多個實例
  4. (可選)防反射攻擊、防序列化破壞

五種寫法完整代碼與對比

1. 餓漢式 — 最簡單、線程安全、類加載即創建


public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();
    private SingletonEager() {}
    public static SingletonEager getInstance() { return INSTANCE; }
}
優點缺點
無鎖、效能最高、JVM 保證線程安全類加載即創建,無延遲加載;若實例從未使用浪費記憶體

2. 懶漢式 + synchronized — 延遲加載,但鎖粗糙


public class SingletonSync {
    private static SingletonSync instance;
    private SingletonSync() {}
    public static synchronized SingletonSync getInstance() {
        if (instance == null) instance = new SingletonSync();
        return instance;
    }
}
優點缺點
延遲加載、線程安全每次調用都同步,性能差 (僅首次需鎖)

3. 雙重檢查鎖 (DCL) — 延遲加載 + 低鎖開銷 (Java 5+)


public class SingletonDCL {
    private static volatile SingletonDCL instance; // 關鍵:volatile
    private SingletonDCL() {}
    public static SingletonDCL getInstance() {
        if (instance == null) {                    // 第一次檢查 (無鎖)
            synchronized (SingletonDCL.class) {    // 鎖類對象
                if (instance == null) {            // 第二次檢查
                    instance = new SingletonDCL(); // 實例化
                }
            }
        }
        return instance;
    }
}

關鍵點:

  • volatile 禁止指令重排序,確保 `instance = new SingletonDCL()` 的三步(分配記憶體、初始化對象、賦值引用)不被重排,避免其他線程看到「半初始化」對象。
  • Java 5+ 記憶體模型修復後 DCL 才可靠;Java 1.4 及以下**不可用**。

4. 靜態內部類 — 延遲加載 + 無鎖 + 線程安全 (推薦)


public class SingletonHolder {
    private SingletonHolder() {}
    private static class Holder {
        static final SingletonHolder INSTANCE = new SingletonHolder();
    }
    public static SingletonHolder getInstance() {
        return Holder.INSTANCE;
    }
}

原理:JVM 類加載機制保證 `Holder` 類首次被主動使用時(調用 `getInstance()` 觸發)初始化,過程線程安全、無需同步。兼具延遲加載與高性能。

5. 枚舉單例 — 最強防禦 (Effective Java 推薦)


public enum SingletonEnum {
    INSTANCE;
    public void doSomething() { ... }
}
// 使用:SingletonEnum.INSTANCE.doSomething();
特性枚舉其他寫法
線程安全✅ JVM 保證視實現
防反射攻擊✅ 構造器私有且 JVM 特殊處理❌ 可用反射強行調用構造器
防序列化破壞✅ 自動處理❌ 需實現 readResolve()
延遲加載✅ 首次使用時初始化視實現
代碼簡潔度✅ 極簡一般

反射破壞單例演示與防禦


// 破壞普通單例
Constructor ctor = SingletonDCL.class.getDeclaredConstructor();
ctor.setAccessible(true);
SingletonDCL s1 = SingletonDCL.getInstance();
SingletonDCL s2 = ctor.newInstance(); // 第二個實例!
System.out.println(s1 == s2); // false

// 防禦:在構造器中檢查
private SingletonDCL() {
    if (instance != null) {
        throw new IllegalStateException("單例已存在,禁止反射創建");
    }
}

枚舉天然免疫:JVM 禁止反射調用枚舉構造器,拋 `IllegalArgumentException`。

序列化破壞與防禦


// 普通單例需實現 readResolve
private Object readResolve() {
    return INSTANCE; // 反序列化時返回同一實例
}

枚舉序列化由 JVM 特殊處理,無需額外代碼。

綜合對比表

維度餓漢式同步懶漢DCL靜態內部類枚舉
線程安全✅✅✅ (Java 5+)✅✅
延遲加載❌✅✅✅✅
性能 (獲取實例)極高低高極高極高
防反射❌❌❌ (可加檢查)❌✅
防序列化破壞❌❌❌ (可加 readResolve)❌✅
代碼複雜度低低中低極低
推薦場景輕量、必用教學演示舊版本兼容**通用首選****安全敏感、新專案**

生產環境選型建議

  1. 新專案、安全敏感場景:枚舉單例 —— 最簡潔、最強防禦、JVM 原生支援。
  2. 一般業務、需延遲加載:靜態內部類 —— 無鎖、線程安全、延遲加載、兼容性好。
  3. 輕量級、啟動即需使用:餓漢式 —— 零開銷、最簡單。
  4. 避免使用:同步懶漢 (性能差)、DCL (除非維護舊代碼/特殊場景需手動控制初始化時機)。

Spring 與單例的關係

  • Spring Bean 預設 `scope="singleton"` 由容器管理生命週期,**非 GoF 單例模式** (容器級單例 vs JVM 級單例)。
  • Spring 單例 Bean **非線程安全**,若有可變狀態需自行同步或改 `prototype`/`ThreadLocal`。
  • 若需全局唯一實例 (如連接池、配置中心客戶端),優先交給 Spring 管理,勿自寫單例。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved