Java 內建多執行緒支援是其區別於傳統語言(如 C/C++ 需依賴 OS API)的重要特性。本文深入淺出解析執行緒基礎概念、進程與執行緒差異、Java 執行緒模型、優先級調度、同步機制與線程通訊,建立完整的併發知識骨架。
| 維度 | 進程 | 執行緒 |
|---|---|---|
| 定義 | 執行中的程式,資源分配基本單位 | CPU 調度基本單位,輕量級進程 |
| 記憶體 | 獨立位址空間,隔離性強 | 共享進程堆/方法區,擁有私有棧/程式計數器 |
| 通訊成本 | 高(IPC:管道、Socket、共享記憶體) | 低(直接讀寫共享變數) |
| 切換開銷 | 大(切換頁表、刷新 TLB) | 小(僅切換棧框架、寄存器) |
| 穩定性 | 崩潰不影響其他進程 | 一條執行緒崩潰可能導致整個進程退出 |
結論:多進程適合隔離性要求高的場景(瀏覽器分頁);多執行緒適合高併發、資料共享密集的場景(Web 伺服器、遊戲引擎)。
事件迴圈不斷檢查事件隊列,CPU 空轉浪費;任一任務阻塞(I/O、睡眠)導致整個程式停滯。
NEW → RUNNABLE → BLOCKED / WAITING / TIMED_WAITING → TERMINATED
synchronized 鎖。Object.wait(), Thread.join(), LockSupport.park()。sleep, wait(timeout), join(timeout))。MIN_PRIORITY~MAX_PRIORITY),預設 5(NORM_PRIORITY)。sleep(), yield(), I/O 阻塞、鎖等待觸發切換。// 實例方法鎖住 this
public synchronized void deposit(int amt) { balance += amt; }
// 靜態方法鎖住 Class 物件
public static synchronized void reset() { ... }
// 同步區塊:鎖住任意物件,縮小鎖範圍
public void transfer(Account target, int amt) {
synchronized(this) { synchronized(target) { ... } } // 注意死鎖風險,需固定鎖順序
}
ReentrantLock 提供可中斷鎖、公平鎖、條件變數(Condition)、嘗試鎖定(tryLock())等進階功能,JDK 1.5+ 首選。
Java 物件天生攜帶一把監視器鎖,synchronized 進入管程,wait/notify 在管程內等待/通知,實現「等待-通知」模式。
| 模式 | 核心 API | 適用場景 |
|---|---|---|
| 共享變數 + 同步 | volatile, synchronized, Lock, Atomic* | 高頻讀寫、簡單狀態同步 |
| 等待-通知 | Object.wait/notify, Condition.await/signal | 生產者-消費者、資源池 |
| 阻塞隊列 | BlockingQueue (ArrayBlockingQueue, LinkedBlockingQueue) | 解耦生產消費、背壓控制 |
| Future/CompletableFuture | FutureTask, CompletableFuture | 非同步計算結果獲取、組合 |
| 訊息傳遞 | Actor 模型、Disruptor、Channel (Go 風格庫) | 高併發、避免共享狀態、跨進程 |
// 方式一:實作 Runnable(推薦,支援 Lambda、線程池複用)
Thread t = new Thread(() -> { /* task */ }, "Worker-1");
t.start();
// 方式二:繼承 Thread(侵入性強,單繼承限制)
class MyThread extends Thread { @Override public void run() { ... } }
new MyThread().start();
核心差異:Runnable 分離任務與執行載體,配合 ExecutorService 實現線程池複用;Thread 綁定任務與執行緒,不利擴展。
理解執行緒模型、同步原理、通訊模式是寫出高效、正確併發程式的前提。實戰中優先使用 java.util.concurrent 高階組件(線程池、並發集合、同步器),減少手動 synchronized/wait/notify 的錯誤風險。