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

Java 單例模式:懶漢式線程安全完整演進

     編輯:Java 綜合教學

Java 單例模式:懶漢式線程安全完整演進

單例模式確保類在全局僅有一個實例,並提供訪問點。懶漢式(延遲初始化)在首次調用時創建實例,但多線程環境下面臨線程安全挑戰。本文演示從不安全到雙重檢查鎖(DCL)的演進過程,並補充字節碼對象鎖原理。

階段 1:不安全的懶漢式

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {          // 線程 A、B 同時進入
            instance = new Singleton();  // 創建兩個實例!
        }
        return instance;
    }
}

問題:instance = new Singleton() 非原子操作(分配內存 → 初始化 → 賦值),指令重排可能導致其他線程看到「半初始化」對象。

階段 2:同步方法(性能差)

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

缺點:每次調用都加鎖,並發性能極低,實際上只有首次需要同步。

階段 3:雙重檢查鎖 (DCL) —— 推薦生產環境

public class Singleton {
    private static volatile Singleton instance = null; // 關鍵:volatile
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次檢查:無鎖,高性能
            synchronized (Singleton.class) {       // 類級別鎖
                if (instance == null) {            // 第二次檢查:防止重複創建
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

關鍵點深度解析

  1. volatile 禁止指令重排:確保 instance = new Singleton() 的「寫入實例引用」發生在「對象初始化完成」之後,其他線程見到的一定是完全構造好的對象。Singleton.class 作為鎖對象:靜態方法無 this,類對象在類加載階段就進入 JVM,天然全局唯一,等同於 Singleton.class.getClassLoader() 級別的鎖。
  2. 雙重檢查意義:首次 null 檢查避免不必要的同步開銷(實例創建後,後續調用直接返回,零鎖)。

補充:獲取字節碼對象的三種方式

// 1. 類名.class(最常用,編譯期確定)
Class clazz1 = Singleton.class;

// 2. 實例.getClass()(運行期確定)
Class clazz2 = new Singleton().getClass();

// 3. Class.forName("全限定類名")(動態加載)
Class clazz3 = Class.forName("com.example.Singleton");

對比:餓漢式(天然線程安全)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton(); // 類加載時創建
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}
維度 餓漢式 懶漢式 (DCL)
初始化時機 類加載 首次調用
線程安全 天然安全 需 volatile + DCL
資源佔用 可能浪費(未用也創建) 按需創建
適用場景 輕量、必用對象 重資源、可選對象

現代最佳實踐:枚舉單例 (Effective Java)

public enum Singleton {
    INSTANCE;
    
    public void doSomething() { /* ... */ }
}

// 調用
Singleton.INSTANCE.doSomething();

優勢:JVM 保證單例、防反射攻擊、防序列化破壞、代碼極簡。首選方案。

總結

  1. 優先使用 枚舉單例。
  2. 需延遲初始化時,使用 volatile + DCL。
  3. 理解 volatile 語義與 Singleton.class 鎖機制,面試與代碼審查必考點。

單例看似簡單,細節決定質量。

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