在多執行緒程式設計中,線程間通訊是協調任務、共享資料的關鍵。Java 提供了多種機制,從基礎的 synchronized 到進階的 wait/notify,各有適用場景與優缺點。本文透過程式碼實戰,深度剖析三種主流通訊方式的原理、陷阱與最佳實踐。
利用 synchronized 關鍵字鎖定共享物件,實現「同一時間僅一執行緒存取關鍵區段」。本質是共享記憶體通訊:誰拿到鎖,誰就能讀寫共享變數,其他執行緒阻塞等待。
public class SharedObject {
public synchronized void methodA() { /* 任務 A */ }
public synchronized void methodB() { /* 任務 B */ }
}
// 線程 A 呼叫 methodA(),線程 B 呼叫 methodB()
// 因鎖定同一 SharedObject 實例,B 必須等 A 釋放鎖才能進入 methodB()
優點:語法簡單、JVM 原生支援、適用於互斥存取。
缺點:粗粒度鎖導致並發度低;無法實現「條件等待」(如:等待隊列非空),只能無條件阻塞。
執行緒 B 不斷在 while(true) 迴圈中檢查條件(如 list.size() == 5),條件滿足才執行後續邏輯。
// 線程 B:消耗 CPU 的無效輪詢
while (true) {
if (sharedList.size() == 5) {
System.out.println("條件滿足,準備處理");
break; // 或拋出異常跳出
}
}
致命缺陷:空轉消耗 CPU。JVM 分配時間片給線程 B,它卻只做條件判斷,不執行實質工作。如同「盯著手機螢幕等電話」,極度浪費算力。生產環境嚴禁使用。
基於 Object 類的 wait()、notify()、notifyAll(),實現「條件不滿足 → 釋放鎖進入 WAITING 狀態 → 條件滿足 → 被喚醒進入 RUNNABLE 狀態」的優雅協作。
// 共享鎖物件
Object lock = new Object();
// 線程 A:等待條件
synchronized (lock) {
while (list.size() != 5) { // 必須用 while 防止虛假喚醒
lock.wait(); // 釋放鎖,進入等待集合
}
// 條件滿足,繼續執行
}
// 線程 B:改變條件並通知
synchronized (lock) {
for (int i = 0; i < 10; i++) {
list.add("item");
if (list.size() == 5) {
lock.notify(); // 喚醒一個等待線程
}
}
}
IllegalMonitorStateException。sleep() 不釋放鎖。notifyAll() 喚醒全部(推薦,避免信號遺漏)。若線程 B 先執行,notify() 發出時線程 A 尚未 wait(),信號丟失。線程 A 後續 wait() 將永久阻塞。
解決:引入狀態標誌位(如 boolean ready = false),線程 B 設置 ready = true 後再 notify();線程 A 檢查 while(!ready) wait()。這正是 Condition / BlockingQueue 內部的設計思想。
JDK 1.5+ java.util.concurrent 提供更高階、更安全的工具:
ArrayBlockingQueue、LinkedBlockingQueue):生產者-消費者首選,內建阻塞與等待。ReentrantLock 配合 newCondition(),支援多條件隊列,比 wait/notify 靈活。// 生產者-消費者標準寫法
BlockingQueue<Task> queue = new LinkedBlockingQueue<>();
// 生產者
queue.put(task); // 滿則阻塞
// 消費者
Task t = queue.take(); // 空則阻塞
| 機制 | 核心原理 | CPU 效率 | 適用場景 | 推薦指數 |
|---|---|---|---|---|
| synchronized | 互斥鎖 | 中 | 簡單互斥、臨界區保護 | ⭐⭐⭐ |
| 輪詢 | 忙等待 | 極低 | 教學演示、極簡單場景 | ❌ 禁用 |
| wait/notify | 條件等待/通知 | 高 | 底層框架開發、特定協作 | ⭐⭐ |
| BlockingQueue 等 | 高階並發組件 | 高 | 業務開發、生產者消費者、任務編排 | ⭐⭐⭐⭐⭐ |
掌握 wait/notify 原理是理解 JUC 組件的基石,但實戰中請優先選用 BlockingQueue、CompletableFuture 等成熟組件,讓專家幫你處理邊界條件與記憶體可見性。