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

Java 多執行緒併發程式設計深度總結:從 JMM 到 Fork/Join 框架

     編輯:Java 相關知識

第一部分:基礎概念與 Java 記憶體模型 (JMM)

進程與執行緒的本質區別

維度進程執行緒
定義程式執行實例,獨立位址空間CPU 調度基本單位,共享進程資源
記憶體獨立堆/棧,隔離性強共享堆/方法區,私有程式計數器/棧
通訊IPC (管道、Socket、共享記憶體)共享變數、wait/notify、管道流
開銷創建/切換成本高輕量級,創建/切換快

JMM 解決的兩大核心問題

  1. 可見性:一個執行緒對共享變數的修改,何時對其他執行緒可見
  2. 有序性:指令重排序對多執行緒語義的影響

導致可見性問題的三大根源

// 1. CPU 時間片輪轉切換
// getNext() 位元碼指令序列 (非原子):
aload_0          // 載入 this
dup              // 複製引用
getfield #12     // 讀取 value
dup_x1           // 複製值到頂部
iconst_1         // 推入常數 1
iadd             // 整數相加
putfield #12     // 寫回 value
// 切換發生在任兩條指令間 → 競態條件

// 2. CPU 多級快取 (L1/L2/L3)
// 寫入先進快取,稍後刷回主存 → 時間窗口內不一致

// 3. 指令重排序 (編譯器/JIT/CPU)
// 單執行緒語義不變,多執行緒下可能觀測到異常順序

第二部分:同步機制層層遞進

Layer 1: volatile - 輕量級可見性保證

// 正確用法:狀態旗標
class Worker {
    private volatile boolean done = false;
    public void setDone(boolean done) { this.done = done; }
    public void work() { while (!done) { /* 執行任務 */ } }
}

// 錯誤用法:複合操作非原子
class Counter {
    public volatile static int count = 0;
    public static void inc() { count++; } // 讀-改-寫三步,非原子!
}
volatile 語義:寫入後強制刷回主存;讀取前使快取失效重讀主存。
禁止指令重排越過 volatile 讀寫 (JSR-133 happens-before 規則)

Layer 2: synchronized - 互斥鎖與完整語義

// 三種鎖定範圍
public synchronized void method() { }           // 實例鎖 (this)
public static synchronized void staticMethod() { } // 類鎖 (Class對象)
public void blockSync() {
    synchronized(lockObject) { }                // 任意物件鎖
}
synchronized 語義包含:互斥性 + 可見性 (鎖釋放前刷主存,獲取後失效快取) + 有序性 (禁止重排出同步塊)

Layer 3: wait/notify - 執行緒協作機制

// 生產者-消費者經典模式
class Buffer {
    private Object item;
    
    public synchronized void put(Object item) {
        while (this.item != null) wait(); // 滿了等待
        this.item = item;
        notifyAll(); // 喚醒消費者
    }
    
    public synchronized Object take() {
        while (item == null) wait(); // 空了等待
        Object result = item;
        item = null;
        notifyAll(); // 喚醒生產者
        return result;
    }
}
關鍵點:wait() 釋放鎖進入等待集;notify() 隨機喚醒一個;notifyAll() 喚醒所有。
必須在 synchronized 內調用,否則拋出 IllegalMonitorStateException

Layer 4: Thread 狀態控制

// 睡眠:不釋放鎖!切勿在 synchronized 內 sleep
Thread.sleep(1000); 

// 等待子執行緒結束
thread.join(); // 可帶超時

// 中斷請求 (協作式中斷)
thread.interrupt(); // 設置中斷標記
// 被中斷方處理:
try { Thread.sleep(1000); } 
catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); // 重新設置標記
    // 清理並退出
}

// yield():禮讓 CPU,不保證切換,測試用暴露併發bug

第三部分:非阻塞同步 - CAS 與 Atomic 包

CAS (Compare-And-Swap) 核心原理

// 硬體指令:原子性比較並交換
// 伪代碼:
boolean compareAndSet(int expect, int update) {
    if (memoryValue == expect) {
        memoryValue = update;
        return true;
    }
    return false;
}

// 自旋重試模式
public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next)) return current;
    }
}

Atomic 包三大類工具類

類別代表類適用場景
基本型別原子變數AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference計數器、旗標、單一物件引用
陣列原子操作AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray共享陣列元素原子更新
欄位更新器 (反射)AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater第三方類別無法修改時,針對 volatile 欄位

實戰:無鎖計數器

class AtomicIdGenerator {
    private final AtomicInteger counter = new AtomicInteger(0);
    public int getNext() { return counter.getAndIncrement(); }
}

第四部分:高階同步工具 - java.util.concurrent

Lock 接口與 ReentrantLock

Lock lock = new ReentrantLock(true); // 公平鎖
lock.lock();
try {
    // 臨界區
} finally {
    lock.unlock(); // 必須在 finally 釋放
}

// 進階功能
lock.lockInterruptibly(); // 可中斷獲取鎖
lock.tryLock(); // 非阻塞嘗試
lock.tryLock(5, TimeUnit.SECONDS); // 帶超時嘗試

ReadWriteLock - 讀寫分離

ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();  // 共享鎖,多讀並行
Lock writeLock = rwLock.writeLock(); // 排他鎖,寫寫/讀寫互斥

// 適用:讀多寫少場景 (配置、快取)
readLock.lock();
try { return cache.get(key); } 
finally { readLock.unlock(); }

Condition - 更靈活的等待/通知

Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();

// 生產者
lock.lock();
try {
    while (queue.size() == capacity) notFull.await();
    queue.add(item);
    notEmpty.signal();
} finally { lock.unlock(); }

核心同步器 AQS (AbstractQueuedSynchronizer)

AQS 是 ReentrantLock、Semaphore、CountDownLatch 等同步器的基礎框架。
核心:state (int/long) 表示共享資源狀態 + FIFO 等待佇列 + CAS 更新 state。
兩種模式:獨佔模式 (tryAcquire/tryRelease) + 共享模式 (tryAcquireShared/tryReleaseShared)

第五部分:高階同步物件 - 開箱即用

Semaphore - 信號量 (資源池控制)

Semaphore semaphore = new Semaphore(2, true); // 2個許可證,公平模式

// 獲取資源
semaphore.acquire(); // 阻塞
semaphore.tryAcquire(); // 非阻塞

// 釋放資源
semaphore.release();

CountDownLatch - 倒數閂 (一次性等待)

CountDownLatch latch = new CountDownLatch(3); // 等待3個任務

// 工作執行緒
new Thread(() -> { doWork(); latch.countDown(); }).start();

// 主執行緒等待
latch.await(); // 阻塞直到計數歸零

CyclicBarrier - 循環柵欄 (可重用、可攜帶動作)

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("三方均就緒,執行彙總邏輯");
});

// 每個參與者
barrier.await(); // 等待其他方

Exchanger - 資料交換器 (雙方換手)

Exchanger<StringBuilder> exchanger = new Exchanger<>();

// 線程A
StringBuilder data = exchanger.exchange(new StringBuilder("A的資料"));

// 線程B
StringBuilder data = exchanger.exchange(new StringBuilder("B的資料"));

第六部分:併發集合 - 高效能資料結構

介面/類別特性適用場景
ConcurrentHashMap分段鎖/ CAS,高併發讀寫,弱一致性迭代器共享快取、計數器、註冊表
CopyOnWriteArrayList寫時複製,讀無鎖,迭代器快照隔離事件監聽器列表、配置白名單 (讀多寫極少)
BlockingQueue阻塞式佇列,生產者-消費者天然支援任務佇列、訊息總線、線程池工作佇列
ConcurrentLinkedQueue非阻塞無界佇列,CAS 實現高吞吐非阻塞傳遞

第七部分:執行緒池與任務執行框架

核心介面體系

Executor → ExecutorService → ScheduledExecutorService
    ↓              ↓                    ↓
execute()    submit()/invokeAll()   scheduleAtFixedRate()

實戰:優雅關閉執行緒池

ExecutorService executor = Executors.newFixedThreadPool(10);

public void shutdownGracefully() {
    executor.shutdown(); // 不再接受新任務
    try {
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow(); // 強制中斷執行中任務
            executor.awaitTermination(60, TimeUnit.SECONDS);
        }
    } catch (InterruptedException ex) {
        executor.shutdownNow();
        Thread.currentThread().interrupt();
    }
}

CompletionService - 異步結果收集

ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<Result> completion = new ExecutorCompletionService<>(executor);

for (Task task : tasks) {
    completion.submit(task);
}

for (int i = 0; i < tasks.size(); i++) {
    Future<Result> future = completion.take(); // 阻塞取得最快完成者
    process(future.get());
}

第八部分:Java 7+ 新特性 - Fork/Join 與 Phaser

Fork/Join 框架 - 分治演算法並行化

// 任務定義
class MaxTask extends RecursiveTask<Long> {
    private final long[] array;
    private final int lo, hi;
    private static final int THRESHOLD = 10000;

    @Override
    protected Long compute() {
        if (hi - lo < THRESHOLD) {
            return sequentialMax(); // 基礎情況:循序計算
        }
        int mid = (lo + hi) >>> 1;
        MaxTask left = new MaxTask(array, lo, mid);
        MaxTask right = new MaxTask(array, mid, hi);
        left.fork(); // 非同步提交子任務
        return Math.max(right.compute(), left.join()); // 合併結果
    }
}

// 執行
ForkJoinPool pool = new ForkJoinPool();
Long max = pool.invoke(new MaxTask(array, 0, array.length));
核心優勢:工作竊取演算法 - 閒置執行緒主動竊取其他執行緒佇列任務,減少等待,最大化 CPU 利用率

Phaser - 多階段同步器

Phaser phaser = new Phaser(1); // 主線程註冊

// 階段1: 解析 HTML
phaser.register(); // 子任務註冊
new Thread(() -> { parseHtml(); phaser.arriveAndAwaitAdvance(); }).start();

// 階段2: 下載圖片
phaser.arriveAndAwaitAdvance(); // 主線程等待階段1完成
// ... 子任務下載 ...

// 階段3: 完成
phaser.arriveAndDeregister(); // 所有任務退出

第九部分:ThreadLocal - 執行緒封閉變數

// 每個執行緒獨立的 ID 生成器
class ThreadLocalIdGenerator {
    private static final ThreadLocal<IdGenerator> local = 
        ThreadLocal.withInitial(IdGenerator::new);
    
    public static int getNext() { return local.get().getNext(); }
}

// 實際應用:資料庫連線持有者、Spring 事務管理、Request 上下文

總結:技術選型決策樹

需要同步嗎?
├── 否 → 無需處理 (不可變物件、執行緒封閉)
└── 是 → 是否為簡單狀態旗標?
    ├── 是 → volatile
    └── 否 → 是否為計數器/單值更新?
        ├── 是 → AtomicInteger/Long/Reference
        └── 否 → 是否為讀多寫少?
            ├── 是 → ReadWriteLock / ConcurrentHashMap / CopyOnWriteArrayList
            └── 否 → 是否為生產者-消費者?
                ├── 是 → BlockingQueue / Semaphore
                └── 否 → 是否需複雜條件等待?
                    ├── 是 → Lock + Condition / Phaser
                    └── 否 → synchronized (最後手段,簡單可靠)

現代 Java 併發開發應優先使用 java.util.concurrent 高階工具,僅在框架無法滿足時降級至 Atomic/Locks,最後才考慮 synchronized/volatile 等低階原語。理解 JMM 與 happens-before 規則是正確推理併發程式行為的基石。

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