多線程環境下,對象從構建到被其他線程可見的「發佈」過程若處理不當,會導致可見性問題、部分構造狀態洩露。本文系統梳理三大核心模式:棧封閉、不可變性、安全發佈四種模式。
public void processRequest(Request req) {
// 局部變量:每線程獨有棧幀,天然隔離
List localList = new ArrayList<>();
Map localMap = new HashMap<>();
// 業務邏輯...
localList.add(req.getParam());
// 方法返回,局部變量隨棧幀銷毀,無發佈風險
}
核心原則:對象引用永不逃逸出方法/線程邊界。無同步開銷,性能最優。
模式 代碼示例 適用場景 內存語義
1. 靜態初始化 public static final Config INSTANCE = new Config();單例、常量、類加載階段構造 Class 初始化鎖保證 Happens-Before
2. volatile 引用 private volatile Config config;運行期熱更新、單寫多讀 volatile 寫-讀 Happens-Before
3. final 域 public final Config config = new Config();構造後不可變引用 final 域寫入-構造函數結束 Happens-Before (JMM)
4. 鎖保護 synchronized(map) { map.put(k, v); }可變共享狀態、複合操作 解鎖-加鎖 Happens-Before
反面教材:不安全發佈
// 危險:非 final、非 volatile、無鎖
public Holder holder;
public void initialize() {
holder = new Holder(42); // 線程 A
}
// 線程 B 可能看到 holder != null 但 holder.n == 0 (部分構造狀態)
public void use() {
if (holder != null) {
System.out.println(holder.n);
}
}
實戰檢查清單
- 對象是否需要跨線程共享?否 → 棧封閉
- 共享對象構造後是否不可變?是 → 不可變模式 + 任意發佈
- 可變共享對象 → 必須選擇四種安全發佈模式之一
- 檢查構造過程中 this 是否逃逸 (註冊回調、啟動線程、拋出異常)
- 使用
@ThreadSafe/@NotThreadSafe/@GuardedBy 註解文檔化線程安全承諾
掌握發佈與逸出核心概念,配合 JMM Happens-Before 規則,是編寫正確並發程序的基石。《Java Concurrency in Practice》第 3 章詳述此體系,強烈建議精讀。