多線程程式設計是 Java 開發者必修課,但僅掌握 synchronized、wait/notify 等底層 API 遠遠不夠。真正的挑戰在於:如何將這些基礎機制組合成可靠、高效、可維護的協作模式?本文梳理五大經典線程協作模式,並探討設計模式視角下的多線程架構思維。
假設系統中有兩個線程需要協作,根據業務場景不同,會衍生出截然不同的協作契約:
場景:線程嘗試進入監視器(獲取物件鎖),若已被佔用,選擇「立即返回/放棄」而非等待。
應用:非關鍵性操作的快速失敗、避免阻塞、防止重複提交。
關鍵點:使用 tryLock() 或 synchronized 非阻塞檢查。
場景:兩個線程需按特定順序執行,而非依賴作業系統調度。
應用:任務流水線、階段性計算、有序資源存取。
關鍵點:引入專門的調度物件(而非直接用鎖),封裝執行順序邏輯。
場景:多線程同時讀取共享資源安全,但寫入需獨佔。
應用:快取、配置資料、高讀低寫場景。
關鍵點:ReentrantReadWriteLock 允許並發讀、獨佔寫,大幅提升吞吐量。
場景:一個線程產生資料,另一個線程處理資料,兩者透過緩衝隊列解耦。
應用:日誌非同步寫入、訊息佇列、任務分發。
關鍵點:BlockingQueue 實現背壓控制,wait/notify 或 Condition 協調。
場景:線程結束非由強制殺死,而是透過「請求終止 → 清理資源 → 正常退出」兩階段完成。
應用:優雅關閉服務、釋放連線、持久化狀態。
關鍵點:volatile boolean shutdownRequested + interrupt() 喚醒阻塞操作。
實際系統中,這五種模式極少孤立存在。一個成熟的多線程架構通常是它們的組合:
書中提到《孫子兵法》可視為古老的設計模式集——「在特定條件下,用特定方式高效解決問題」。這啟示我們:
BlockingQueue 已封裝好生產者-消費者)自 Java 5 起,java.util.concurrent 提供了工業級組件,讓模式落地更簡單:
| 模式 | 核心類/介面 | 說明 |
|---|---|---|
| 生產者-消費者 | BlockingQueue, ExecutorService | 無需手寫 wait/notify,支援有界隊列、拒絕策略 |
| 讀寫鎖 | ReentrantReadWriteLock, StampedLock | StampedLock 更優化樂觀讀效能 |
| 調度器 | ScheduledExecutorService, CompletableFuture | 支援延遲、週期、鏈式非同步編排 |
| 兩階段終止 | Thread.interrupt(), Future.cancel() | 配合 try-with-resources 確保資源釋放 |
| Balking | ReentrantLock.tryLock(), Semaphore.tryAcquire() | 非阻塞嘗試獲取許可 |
多線程程式設計的本質是協作約束的建模。掌握這五大核心模式,配合 JUC 現代工具箱,能讓你從「寫對鎖」進化到「設計正確的協作契約」。下次面對並發需求時,請先問自己:這是生產消費?讀寫分離?還是需要有序調度?找到模式,代碼自然順暢。