多線程是利用多核 CPU、實現高吞吐、低延遲的關鍵。本文系統梳理 Java 多線程核心知識點:線程生命週期、同步機制、內存模型、線程池原理、鎖優化、同步工具類、常見坑點,並深度剖析 `ThreadPoolExecutor` 核心源碼,幫助構建完整並發知識體系。
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()` 正常返回或拋未捕獲異常 | 線程結束 |
將共享數據及其操作方法封裝在同一「資源類」中,對外僅暴露同步方法,保證數據與鎖的綁定關係不被外部破壞。
public ThreadPoolExecutor(
int corePoolSize, // 核心線程數
int maximumPoolSize, // 最大線程數
long keepAliveTime, // 非核心線程存活時間
TimeUnit unit, // 時間單位
BlockingQueue workQueue, // 任務隊列
ThreadFactory threadFactory, // 線程工廠
RejectedExecutionHandler handler // 拒絕策略
)
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 或池關閉,拒絕
}
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); }
}
| 工具 | 核心特性 | 典型場景 |
|---|---|---|
| ReentrantLock | 可中斷、公平/非公平、Condition 多條件變量 | 替代 synchronized 複雜場景 |
| ReadWriteLock | 讀讀共享、讀寫/寫寫互斥 | 讀多寫少緩存 |
| Condition | await/signal 綁定 Lock,支持多路通知 | 有界隊列 notFull/notEmpty |
| Semaphore | 計數信號量,控制並發訪問數 | 限流、資源池 |
| CyclicBarrier | 攔截器,N 線程齊聚再放行,可重用 | 多階段計算同步 |
| CountDownLatch | 倒計時門闂,不可重用 | 主線程等待多子線程完成 |
| Exchanger | 雙線程交換數據 | 生產者-消費者雙緩衝 |
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(); }
}
}
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(); }
}
}
Java 多線程知識體系龐大,核心在於:理解線程狀態流轉、掌握 JMM 內存語義、熟練使用 JUC 工具類、讀懂線程池源碼。實戰中遵循「優先用高級併發工具、避免手工 synchronized/wait、共享數據封裝內聚、鎖粒度最小化、線程池參數根據業務類型(CPU 密集/IO 密集)科學配置」,才能寫出高性能、穩定的併發程序。