Java 多線程是高併發程式設計的基石,但也充滿陷阱。本文彙整 23 條核心知識點,涵蓋記憶體模型、線程狀態、同步機制、效能優化等,助你構建正確、高效的併發程式。
JVM 將共享變量存於主記憶體(堆),每條線程擁有私有工作記憶體(棧幀區),操作共享變量時需經「讀取→載入→運算→存儲→寫回」步驟,volatile 可保證可見性。
NEW → RUNNABLE(Ready/Running) → BLOCKED(鎖等待) → WAITING/TIMED_WAITING(wait/sleep/join) → TERMINATED。
線程啟動順序 ≠ 執行順序。若需控制,可用 setPriority()、join()、CountDownLatch 等同步工具。
多線程讀寫同一變量時,需用 synchronized、Lock、原子類等保護,否則會出現競態條件。
未終止的線程會被線程組/執行緒池引用,導致無法被垃圾回收,長時間運行的應用需注意線程洩漏。
setDaemon(true) 標記的線程,在所有非守護線程結束時 JVM 自動退出,適合後台輔助任務(如 GC、定時清理)。
synchronized 鎖 this。synchronized 鎖 Class 對象。「一處同步,處處同步」,否則保護失效。
確認無衝突的操作(如唯讀、線程局部變量)不加鎖,減少上下文切換開銷。
若寫方法已同步,讀方法也應同步,否則可能讀到舊值(缺乏 happens-before 關係)。
synchronized 不可繼承子類重寫父類同步方法時,同步修飾符不會自動繼承,需顯式添加。
read()、accept())。BLOCKED)。wait()、sleep()、join()。i++ 非原子i = i + 1 包含讀取、修改、寫回三步,多線程下會丟失更新。long/double 非 volatile 時甚至連讀寫都非原子。
class Counter extends Thread {
private int i;
public synchronized int read() { return i; }
public synchronized void increment() { i++; }
}
或使用 AtomicInteger、LongAdder 等無鎖類。
volatile 關鍵字保證讀寫直接作用於主記憶體,適用於狀態標記、單次寫入多次讀取場景,但不保證複合操作原子性。
yield() 主動讓出 CPU僅讓同優先級線程有機會執行,不釋放鎖,適合計算密集型任務主動插隊。
sleep() vs wait()| 特性 | sleep() | wait() |
|---|---|---|
| 所屬類 | Thread | Object |
| 釋放鎖 | 否 | 是 |
| 喚醒方式 | 時間到/中斷 | notify()/notifyAll()/中斷 |
| 調用前提 | 任意位置 | 必須在同步塊/方法中 |
wait(timeout)帶超時的 wait 可避免無限期阻塞,配合 notifyAll() 防止信號遺漏。
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(out);
// 生產者寫 out,消費者讀 in
tryLock(timeout) 避免無限等待。stop()、suspend()、resume()、destroy()(已棄用)。高優先級線程獲得更多 CPU 時間片,但低優先級不會餓死(時間片輪轉)。
主要提供批量操作(interrupt()、setDaemon()),現代開發較少直接使用。
wait/sleep/yield 釋放 CPU。sleep 時間減少輪詢頻率。掌握上述要點,能讓你在面對複雜併發場景時,從容選擇正確的同步策略、避開常見坑洞,編寫出穩健高效的多線程程式。