並發編程是利用多核 CPU 提升吞吐量的關鍵,也是 Java 工程師進階必經之路。本文系統梳理:線程優勢與代價、創建啟動方式、競態條件與臨界區、線程安全與逃逸分析、不可變模式、Java 記憶體模型 (JMM)、synchronized 鎖優化、線程通信、死鎖預防與診斷,並附帶最佳實踐清單。
| 優勢 | 說明 |
|---|---|
| 資源利用率更高 | I/O 等待時 CPU 可切換執行其他線程(如讀磁盤 5ms + 處理 2ms → 並行讀取兩文件總耗時 12ms vs 串行 14ms) |
| 建模更簡單 | 每個任務一個線程,邏輯獨立,避免手動狀態機管理 |
| 響應更快 | 主線程分派任務給工作者線程,立即返回處理新請求(如 Web 服務器、GUI 應用) |
class MyThread extends Thread {
@Override public void run() { System.out.println("Running: " + getName()); }
}
new MyThread().start(); // 必須呼叫 start(),而非 run()
class MyRunnable implements Runnable {
@Override public void run() { System.out.println("Running"); }
}
new Thread(new MyRunnable()).start();
優勢:分離任務邏輯與執行機制,易於提交給線程池、實現多介面。
new Thread(new MyRunnable()).run(); // 錯誤:在當前線程同步執行 run(),未啟動新線程
Thread t = new Thread(runnable, "Worker-1");
t.start();
String name = Thread.currentThread().getName(); // 當前線程名
class Counter { long count = 0;
void add(long v) { count = count + v; } // 非原子:讀-改-寫三步
}
// 兩線程並發 add(2)、add(3) 可能結果 3 或 5,而非 5
| 支柱 | 核心原則 | 典型實現 |
|---|---|---|
| 封閉性 | 資源不逃逸出線程 | 局部變數、ThreadLocal、棧封閉 |
| 不可變性 | 構造後狀態不變 | final 欄位、無 setter、防御性複製、String、包裝類 |
| 同步機制 | 協調訪問順序 | synchronized、Lock、volatile、原子類、並發集合 |
若資源的「創建、使用、銷毀」全在同一線程內完成,且永不脫離該線程控制,則該資源使用是線程安全的。
注意:物件線程安全 ≠ 物件引用線程安全。持有不可變物件的可變引用(如 volatile Immutable ref)仍需同步。
final class ImmutableValue {
private final int value;
public ImmutableValue(int v) { this.value = v; }
public int getValue() { return value; }
public ImmutableValue add(int v) { return new ImmutableValue(value + v); } // 返回新實例
}

volatile、synchronized、final 保證。happens-before 規則定義偏序關係。| 類型 | 監視器對象 | 鎖粒度 |
|---|---|---|
實例方法 synchronized void m() | this | 實例級 |
靜態方法 static synchronized void m() | Class 物件 | 類級 |
實例方法塊 synchronized(this) {} | this | 塊級(可縮小範圍) |
靜態方法塊 synchronized(MyClass.class) {} | Class 物件 | 塊級 |
// 傳統 Object 監視器
synchronized (lock) {
while (!condition) lock.wait(); // 釋放鎖並阻塞
// 條件滿足執行
lock.notifyAll(); // 喚醒所有等待線程
}
// Lock + Condition(更靈活,支援多條件隊列)
Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
lock.lock();
try {
while (queue.isEmpty()) notEmpty.await();
take();
notFull.signal();
} finally { lock.unlock(); }
打破任一條件即可預防死鎖。
lock.tryLock(timeout),失敗釋放已持有鎖重試。ThreadMXBean.findDeadlockedThreads() 定期掃描,配合 jstack 分析。
jstack > thread_dump.txt
# 搜尋 "Found one Java-level deadlock" 或 "BLOCKED on ... owned by"
jstack、VisualVM、Arthas 排查阻塞、死鎖、CPU 熱點。並發編程的本質是在共享資源訪問上協調時序。掌握 JMM 語義、鎖機制、線程通信模式與死鎖預防,配合現代高階 API(並發包、CompletableFuture、虛擬線程),即可構建高吞吐、低延遲、穩定可靠的 Java 系統。