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

Java 並發編程核心理論:共享性、互斥性、原子性、可見性、有序性與 JMM 實戰

     編輯:Java 程式設計入門

前言

並發編程的本質是**在共享數據、多執行緒、有限資源**三約束下協調執行。Java 記憶體模型 (JMM) 定義了**線程與主內存交互規範**,是理解 `volatile`、`synchronized`、`final`、並發容器語義的理論基石。本文系統剖析**五大核心特性、JMM happens-before 規則、指令重排類型、同步工具語義、實戰避坑**,構建並發編程知識骨架。

一、五大核心特性深度解析

1. 共享性

  • 定義:多執行緒可訪問同一堆內存變量 (實例域、靜態域、數組元素)。
  • 本質:堆內存全局可見,棧幀線程私有。
  • 風險:非線程安全操作 (如 `count++`) 導致**競態條件**。
  • 對策**:封閉共享** (ThreadLocal、棧封閉、不可變對象) → **同步訪問** (鎖、CAS、並發容器)。

2. 互斥性

  • 定義:同一時刻僅允許一個執行緒進入臨界區。
  • 實現**:監視器鎖** (`synchronized`)、**顯式鎖** (`ReentrantLock`)、**信號量** (`Semaphore`)、**讀寫鎖** (`ReentrantReadWriteLock`)。
  • 粒度**:對象鎖、類鎖、方法鎖、代碼塊鎖、分段鎖 (ConcurrentHashMap JDK 7)、分離鎖 (Striped)。
  • 活躍性風險**:死鎖、活鎖、飢餓 → **按序獲取鎖、超時機制、避免嵌套鎖**。

3. 原子性

  • 定義**:操作不可分割,要麼全執行、要麼全不執行,無中間狀態可見。
  • 基本型原子性**:`long`/`double` 非 `volatile` 時 **64 位寫入非原子** (JMM 允許拆兩次 32 位寫入);其他基本型、引用賦值原子。
  • 複合操作非原子**:`i++` (讀-改-寫)、`check-then-act` (如 `if (map.get(key)==null) map.put(key,val)`)。
  • 實現方案**:
    1. **鎖** (`synchronized`/`Lock`):悲觀、阻塞、簡單通用。
    2. **CAS** (`Atomic*`、`LongAdder`、`Unsafe.compareAndSwap*`):樂觀、非阻塞、高併發優、ABA 問題 (用 `AtomicStampedReference` 解決)。
    3. **不可變對象** (`final`、`String`、`Record`):天然原子、無鎖。

4. 可見性

  • 定義**:一個執行緒對共享變量的修改,對其他執行緒**立即可見**。
  • JMM 工作內存模型**:每個執行緒有**工作內存** (CPU 緩存/寄存器副本),讀寫先經工作內存,再同步回主內存。
  • 導致不可見**:編譯器優化 (寄存器緩存)、CPU 緩存一致性協議延遲、指令重排。
  • 保證可見性**:
    1. `volatile`:寫入**立即刷回主存** + **禁止指令重排** (StoreStore/StoreLoad 屏障)。
    2. `synchronized`:解鎖前**刷回主存**,加鎖後**失效工作內存重讀**。
    3. `final`:構造函數結束前**寫入可見**,無需同步 (JMM 特殊規則)。
    4. 並發容器 (`ConcurrentHashMap`、`CopyOnWriteArrayList`):內部 `volatile`/`Unsafe` 保證。

5. 有序性

  • 定義**:程序執行順序符合代碼邏輯順序 (單執行緒語義)。
  • 重排序類型**:
    1. **編譯器優化重排** (JIT):無數據依賴調換順序。
    2. **指令級並行重排** (CPU):流水線、超標量、亂序執行。
    3. **內存系統重排** (緩存/寫緩衝區):寫入不同緩存行順序不定。
  • as-if-serial 語義**:單執行緒下重排不改變結果 (含異常)。
  • 保證有序性**:
    1. `volatile`:寫屏障 (StoreStore/StoreLoad) + 讀屏障 (LoadLoad/LoadStore)。
    2. `synchronized`:鎖語義隱含 `happens-before` (解鎖 hb 後續加鎖)。
    3. `final`:構造函數內寫入 hb 構造函數返回 (安全發布)。

二、JMM Happens-Before 八大規則

規則描述典型應用
程序順序單執行緒代碼順序 hb基礎
監視器鎖解鎖 hb 後續同鎖加鎖`synchronized` 可見性/有序性
volatile 變量寫 hb 後續讀狀態標誌、DCL
線程啟動`Thread.start()` hb 線程內動作線程安全發布
線程終止線程內動作 hb `Thread.join()` 返回主線程獲取結果
線程中斷`interrupt()` hb 被中斷線程檢測到優雅關閉
final 域構造函數寫 final hb 構造函數返回 hb 讀 final不可變對象安全發布
傳遞性A hb B, B hb C → A hb C鏈式推導

三、經典並發 Bug 復現與修復

1. 計數器非原子


// Bug
int count = 0;
for (int i=0;i<10;i++) new Thread(()->{ for(int j=0;j<1000;j++) count++; }).start();
// 結果 < 10000

// Fix 1: AtomicInteger
AtomicInteger count = new AtomicInteger();
count.incrementAndGet();

// Fix 2: LongAdder (高併發更優)
LongAdder count = new LongAdder();
count.increment();

2. 雙重檢查鎖 (DCL) 半初始化


// Bug (無 volatile)
static Singleton instance;
public static Singleton get() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) instance = new Singleton(); // 分配→初始化→賦值,可能重排
        }
    }
    return instance;
}

// Fix: volatile 禁止重排
static volatile Singleton instance;

3. 發布逃逸 (this 逃逸)


// Bug: 構造函數中啟動線程/註冊監聽器,this 未完全構造即可見
public class EventSource {
    public EventSource() {
        new Thread(() -> doSomething(this)).start(); // 危險!
    }
}

// Fix: 工廠方法 + 完全構造後發布
public static EventSource create() {
    EventSource es = new EventSource();
    new Thread(() -> doSomething(es)).start();
    return es;
}

四、同步工具語義速查表

工具互斥可見性有序性適用場景
`synchronized`✅✅✅通用臨界區、方法級同步
`volatile`❌✅✅狀態標誌、單次寫入、DCL
`ReentrantLock`✅✅✅可中斷、超時、公平、條件變量
`Atomic*`✅ (CAS)✅✅計數器、引用更新、累加器
`LongAdder`✅ (CAS+分段)✅✅高併發計數、統計指標
`ConcurrentHashMap`分段/節點 CAS✅✅高併發 Map、緩存
`CopyOnWriteArrayList`寫時複製✅✅讀多寫少、快照遍歷
`ThreadLocal`隔離 (無共享)--線程封閉、上下文傳遞

五、實戰最佳實踐清單

  1. 優先用不可變對象:`final` 域、Record、防禦性複製、集合 `Collections.unmodifiable*`。
  2. 縮小鎖範圍:只鎖核心邏輯,避免 I/O、日志、RPC 在鎖內。
  3. 用並發容器替代手工同步:`ConcurrentHashMap`、`LongAdder`、`BlockingQueue`。
  4. 避免過早優化:先正確、再測壓、再優化熱點 (JFR/Async Profiler 定位)。
  5. 線程池必配有界隊列 + 拒絕策略:防 OOM、防任務積壓、支撐背壓。
  6. 虛擬線程 (JDK 21+) 改寫阻塞 IO**:`Executors.newVirtualThreadPerTaskExecutor()`,無需 Reactive 改造。
  7. 嚴禁雙重檢查鎖無 volatile**:JDK 5+ 必須加 `volatile`。
  8. 發布對象必須安全**:構造函數不啟動線程、不註冊回調、用工廠/構建器模式。

💡 核心心法:共享是根源,互斥是手段,原子/可見/有序是 JMM 給出的三大保證。寫並發代碼時,先問:**這個變量共享嗎?操作原子嗎?寫入需可見嗎?順序敏感嗎?** 選最簡單滿足條件的工具。**能用不可變別用鎖,能用並發容器別手寫同步,能用虛擬線程別手寫異步。**

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