多線程編程面臨諸多協作場景。與其每次重新發明輪子,不如掌握經典並發設計模式。本文提煉五大核心模型,覆蓋 80% 以上業務場景。理解模式本質,比死記代碼更有價值。
場景: 線程嘗試進入監視器(獲取對象鎖),若鎖被佔用,選擇直接返回/拋異常而非等待。
適用: 非關鍵操作、幂等重試、緩存失效重算、「搶單」業務(搶不到立即返回失敗)。
代碼骨架:
public void doSomething() {
synchronized (lock) {
if (busy) return; // 或 throw new BusyException();
busy = true;
}
try { /* 執行任務 */ } finally {
synchronized (lock) { busy = false; }
}
}
場景: 多線程需有序執行,而非依賴 OS 隨機調度。引入調度器對象協調執行順序。
本質: 操作系統調度器的用戶態映射。Java 中常用 wait/notify、Condition、Semaphore、CyclicBarrier 實現。
典型: 線程池任務隊列、定時調度器、管道流水線。
場景: 共享數據「讀多寫少」。允許多線程並發讀,但寫操作獨佔。
Java 實現: ReentrantReadWriteLock、StampedLock (JDK 8+)。
注意: 寫鎖降級為讀鎖需謹慎,避免數據竄改;StampedLock 樂觀讀進一步提升性能。
場景: 一線程產生數據(下載圖片),另一線程消費數據(顯示圖片)。
核心組件: 共享緩衝隊列(阻塞隊列 BlockingQueue)。
優勢:
wait/notify。BlockingQueue queue = new LinkedBlockingQueue<>(100);
// 生產者
queue.put(task); // 滿則阻塞
// 消費者
Task task = queue.take(); // 空則阻塞
場景: 線程需在滿足特定條件(如任務隊列清空、檔案寫完)後才能安全退出,而非暴力 stop()。
兩階段:
shutdownRequested = true,中斷阻塞操作(interrupt())。run()。volatile boolean shutdownRequested = false;
public void shutdown() { shutdownRequested = true; thread.interrupt(); }
public void run() {
while (!shutdownRequested && !Thread.interrupted()) {
try { Task task = queue.take(); process(task); }
catch (InterruptedException e) { /* 處理中斷 */ }
}
// 收尾:flush(), close(), saveState()
}
ScheduledExecutorService 融合 Scheduler + Producer-Consumer)。java.util.concurrent 現成組件,而非手寫 wait/notify。作者大膽推測:《孫子兵法》實為古代「設計模式」集大成——特定條件下,以特定策略高效解決問題。只是應用於軍事,且未被系統提煉為軟件工程模式。類比思維是發明發現的通途,或許我們能從傳統文化挖掘更多軟件設計智慧。
掌握 Balking、Scheduler、Read/Write Lock、Producer-Consumer、Two-Phase Termination 五大模式,配合 java.util.concurrent 工具箱,足以應對絕大多數多線程協作挑戰。模式不僅是代碼模板,更是並發思維的結晶。