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

深入解析 Java 單例模式:從基礎實現到線程安全最佳實踐

     編輯:Java 綜合教學

前言:為什麼需要單例模式?

在軟體開發中,單例模式是應用最廣泛的設計模式之一,往往也是初級工程師最先接觸的模式。其核心目的在於確保整個系統中某個類只存在一個實例,並提供全域存取點。這在資源消耗大、需要協調系統行為的場景中尤為關鍵,例如影像載入器、資料庫連線池、配置管理器等。若這些物件被重複建立,不僅浪費記憶體與 CPU,更可能導致狀態不一致。

單例模式的核心定義與適用場景

定義

確保某一類別僅有一個實例,並自行實例化且向整個系統提供該實例。

典型使用場景

  • 建立物件需消耗大量資源(如 I/O、資料庫連線、執行緒池)
  • 系統邏輯上只允許存在單一實例(如日誌管理器、配置中心)
  • 需提供全域統一存取點,避免多實例造成狀態混亂

UML 結構與關鍵實現要素

單例模式 UML 類圖

核心角色:Client(客戶端)與 Singleton(單例類)。

實現單例的四大關鍵點:

  1. 建構子私有化(private),禁止外部 new
  2. 提供公靜態方法(或列舉)回傳唯一實例
  3. 多執行緒環境下確保實例唯一性
  4. 防止反序列化重建物件(可實作 readResolve)

基礎範例:飢漢式單例

public class CEO extends Staff {
    private static final CEO INSTANCE = new CEO(); // 類別載入時即初始化

    private CEO() {} // 私有建構子

    public static CEO getCEO() {
        return INSTANCE;
    }

    @Override
    public void work() {
        // 管理 VP 邏輯
    }
}

特點:類別載入時靜態欄位即完成初始化,天然線程安全,但無法延遲載入,若實例從未被使用會造成資源浪費。

進階實現方式深度解析

1. 懶漢模式(同步鎖版)

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

缺點:每次呼叫 getInstance() 皆需同步,效能低落;首次載入反應較慢。

2. 雙重檢查鎖(DCL)

public class Singleton {
    private volatile static Singleton instance; // volatile 禁止指令重排
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) { // 第一次檢查,避免不必要同步
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次檢查,確保單一建立
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

關鍵細節:volatile 確保 instance = new Singleton() 的三步驟(分配記憶體、初始化物件、指向記憶體)不會被重排為 1-3-2,否則多執行緒可能拿到未初始化完成的物件。JDK 1.5+ 修正記憶體模型後此寫法才可靠。

3. 靜態內部類(推薦)

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

優勢:利用類別載入機制天然保證線程安全、延遲初始化、程式碼簡潔,無同步開銷,是業界公認最佳實踐之一。

4. 列舉單例(最簡潔、防反序列化破壞)

public enum SingletonEnum {
    INSTANCE;
    public void doSomething() { System.out.println("do sth."); }
}

列舉實例建立過程由 JVM 保證線程安全,且天然防止反序列化產生新物件,是《Effective Java》強力推薦的方式。

5. 容器管理單例(適用於多型單例註冊)

public class SingletonManager {
    private static final Map<String, Object> registry = new HashMap<>();
    private SingletonManager() {}
    public static void register(String key, Object instance) {
        if (!registry.containsKey(key)) registry.put(key, instance);
    }
    public static Object getService(String key) {
        return registry.get(key);
    }
}

適用於需統一管理多種單例類型、降低耦合度的框架級場景。

總結與選型建議

實現方式線程安全延遲載入效能推薦指數
飢漢式✅❌高⭐⭐⭐
懶漢式✅✅低⭐
DCL✅ (需 volatile)✅高⭐⭐⭐⭐
靜態內部類✅✅高⭐⭐⭐⭐⭐
列舉✅✅高⭐⭐⭐⭐⭐

核心原則不變:私有建構子 + 靜態工廠方法 + 線程安全保障 + 防序列化破壞。實務上首選靜態內部類或列舉,特殊框架需求可考慮容器管理。

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