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

Java 多線程核心知識體系:從線程狀態到線程池源碼深度解析

     編輯:Java 綜合教學

前言:並發編程的基石

多線程是利用多核 CPU、實現高吞吐、低延遲的關鍵。本文系統梳理 Java 多線程核心知識點:線程生命週期、同步機制、內存模型、線程池原理、鎖優化、同步工具類、常見坑點,並深度剖析 `ThreadPoolExecutor` 核心源碼,幫助構建完整並發知識體系。

一、基礎概念澄清

  • 進程:資源分配單位,獨立內存空間。
  • 線程:調度執行單位,屬於某進程,共享進程內存(堆、方法區),私有棧、程序計數器。
  • Java 線程雙重含義:`java.lang.Thread` 實例(堆對象)+ 執行路徑(棧幀)。

二、線程五大狀態與轉換

NEW (新建) → start() → RUNNABLE (就緒/運行) ↔ BLOCKED (阻塞)
                                    ↑           ↓
                              WAITING/TIMED_WAITING (等待/超時等待)
                                    ↓
                                 TERMINATED (死亡)
狀態觸發條件備註
NEW`new Thread()`未調用 start()
RUNNABLE調用 `start()`含就緒與運行,JVM 不區分
BLOCKED等待 `synchronized` 鎖進入鎖池
WAITING`wait()`/`join()` 無超時進入等待池,需 `notify`/`interrupt`
TIMED_WAITING`sleep()`/`wait(timeout)`/`join(timeout)`超時自動轉 RUNNABLE
TERMINATED`run()` 正常返回或拋未捕獲異常線程結束

三、線程安全與同步機制

3.1 synchronized 核心語義

  • 修飾實例方法 ⇔ `synchronized(this)`
  • 修飾靜態方法 ⇔ `synchronized(Cls.class)`
  • 修飾代碼塊 ⇔ `synchronized(lockObj)`
  • 同一把鎖才能互斥;鎖對象必須為同一實例。

3.2 經驗法則:高內聚封裝共享數據

將共享數據及其操作方法封裝在同一「資源類」中,對外僅暴露同步方法,保證數據與鎖的綁定關係不被外部破壞。

3.3 ThreadLocal:線程隔離變量

  • Key = 當前線程,Value = 線程私有副本。
  • 一個 `ThreadLocal` 只能存一個值;適用於數據庫連接、Session 上下文傳遞。

3.4 多線程共享數據三種模式

  1. 同代碼:實現 `Runnable`,多線程共享同一實例字段。
  2. 不同代碼:各自 `Runnable`,共享外部類字段。
  3. 封裝對象:共享數據+操作方法封裝為獨立對象,作為外部類字段或局部變量,內部類實現 `Runnable` 訪問該對象。

四、JVM 線程內存交互模型 (JMM 簡述)

  • 線程讀寫共享變量:主內存 → 工作內存 (load) → 運算 → 工作內存 → 主內存 (store/write)。
  • `volatile` 保證可見性與有序性,不保證原子性。
  • `synchronized` 保證原子性、可見性、有序性。

五、線程池深度解析:ThreadPoolExecutor 源碼逐層剖析

5.1 七大構造參數

public ThreadPoolExecutor(
    int corePoolSize,           // 核心線程數
    int maximumPoolSize,        // 最大線程數
    long keepAliveTime,         // 非核心線程存活時間
    TimeUnit unit,              // 時間單位
    BlockingQueue workQueue, // 任務隊列
    ThreadFactory threadFactory,       // 線程工廠
    RejectedExecutionHandler handler   // 拒絕策略
)

5.2 核心字段

  • `ctl`:原子整數,高 3 位存 `runState`,低 29 位存 `workerCount`。
  • `workQueue`:任務緩衝隊列(`LinkedBlockingQueue`/`ArrayBlockingQueue`/`SynchronousQueue`)。
  • `workers`:`HashSet` 持有所有工作線程。
  • `mainLock`:全局重入鎖,保護 `workers`、`ctl` 等結構修改。

5.3 execute() 執行流程(核心邏輯)

public void execute(Runnable command) {
    if (command == null) throw new NullPointerException();
    int c = ctl.get();
    // 1. 線程數 < corePoolSize → 創建核心線程執行
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true)) return;
        c = ctl.get();
    }
    // 2. 池 RUNNING 且 入隊成功
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 雙重檢查:若池已關閉或線程數為 0,確保任務被處理
        if (!isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false); // 確保至少一個線程消費隊列
        return;
    }
    // 3. 隊列滿 → 嘗試創建非核心線程
    if (!addWorker(command, false))
        reject(command); // 達到 maximumPoolSize 或池關閉,拒絕
}

5.4 addWorker() 創建線程細節

  1. 持有 `mainLock`,CAS 增加 `workerCount`。
  2. `threadFactory.newThread(worker)` 創建 `Thread`,綁定 `Worker`。
  3. `worker.thread.start()` → `Worker.run()` → 循環 `getTask()` → `runTask()`。

5.5 Worker 運行邏輯

final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    Runnable firstTask; volatile long completedTasks; Thread thread;
    public void run() { runWorker(this); }
}
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask; w.firstTask = null;
    w.unlock(); // 釋放 AQS 鎖,允許中斷
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock(); // 獲取 AQS 鎖,標記執行中
            try {
                beforeExecute(wt, task);
                task.run();
                afterExecute(task, null);
            } catch (RuntimeException x) {
                afterExecute(task, x); throw x;
            } finally {
                w.unlock(); w.completedTasks++; task = null;
            }
        }
    } finally { processWorkerExit(w); }
}

5.6 getTask() 任務獲取與線程回收

  • `SHUTDOWN` 狀態:`poll()` 耗盡隊列。
  • `RUNNING` 且 線程數 > corePoolSize 或 `allowCoreThreadTimeOut`:`poll(keepAliveTime)` 超時返回 null → 線程退出。
  • 否則:`take()` 阻塞等待任務。

六、鎖優化與高級同步工具 (JUC)

工具核心特性典型場景
ReentrantLock可中斷、公平/非公平、Condition 多條件變量替代 synchronized 複雜場景
ReadWriteLock讀讀共享、讀寫/寫寫互斥讀多寫少緩存
Conditionawait/signal 綁定 Lock,支持多路通知有界隊列 notFull/notEmpty
Semaphore計數信號量,控制並發訪問數限流、資源池
CyclicBarrier攔截器,N 線程齊聚再放行,可重用多階段計算同步
CountDownLatch倒計時門闂,不可重用主線程等待多子線程完成
Exchanger雙線程交換數據生產者-消費者雙緩衝

6.1 緩存系統雙重檢查鎖模板

public class MyCache {
    private final Map cache = new HashMap<>();
    private final ReadWriteLock rwl = new ReentrantReadWriteLock();

    public Object getData(String key) {
        rwl.readLock().lock();
        Object value = cache.get(key);
        if (value == null) {
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                if (value == null) { // 二次檢查
                    value = queryDB();
                    cache.put(key, value);
                }
            } finally { rwl.writeLock().unlock(); }
            rwl.readLock().lock();
        }
        try { return value; }
        finally { rwl.readLock().unlock(); }
    }
}

6.2 有界阻塞隊列實現 (生產者-消費者)

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();
    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) notFull.await();
            items[putptr] = x;
            if (++putptr == items.length) putptr = 0;
            ++count; notEmpty.signal();
        } finally { lock.unlock(); }
    }
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) notEmpty.await();
            Object x = items[takeptr];
            if (++takeptr == items.length) takeptr = 0;
            --count; notFull.signal();
            return x;
        } finally { lock.unlock(); }
    }
}

七、常見坑點速查表

  • ❌ `sleep()` 不釋放鎖;✅ `wait()` 釋放鎖。
  • ❌ `notify()` 不釋放鎖,退出同步塊才釋放;✅ 偏好 `notifyAll()` 避免信號丟失。
  • ❌ 死線程不能 `start()` 二次啟動;✅ 用單線程池 `Executors.newSingleThreadExecutor()` 實現重試。
  • ❌ `yield()` 不進入等待/阻塞,僅提示調度器;不可靠。
  • ❌ `join()` 本質是 `wait()`,需處理 `InterruptedException`。
  • ❌ 同步塊過大損害並發;✅ 縮小同步範圍,鎖粒度越細越好。
  • ✅ `CopyOnWriteArrayList` 適合「讀多寫少、遍歷時需快照」場景。
  • ✅ `java.util.concurrent.atomic` 保證基本類型原子操作(CAS)。

總結

Java 多線程知識體系龐大,核心在於:理解線程狀態流轉、掌握 JMM 內存語義、熟練使用 JUC 工具類、讀懂線程池源碼。實戰中遵循「優先用高級併發工具、避免手工 synchronized/wait、共享數據封裝內聚、鎖粒度最小化、線程池參數根據業務類型(CPU 密集/IO 密集)科學配置」,才能寫出高性能、穩定的併發程序。

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