前言:從基礎同步到高級協作控制
Java 多線程編程不僅是 synchronized 與 Thread.start()。掌握線程組管理、屏障同步、守護線程特性、棄用方法風險及死鎖/活鎖診斷,是構建高併發穩定系統的關鍵。本文系統梳理這些高級主題。
一、線程組:層次化管理與調試利器
- 線程創建時指定組:
new Thread(threadGroup, runnable, "name")
- 組可巢狀,形成樹狀結構,類似文件目錄
- 核心 API:
activeCount()、enumerate(Thread[])、interrupt() 批量中斷、uncaughtException() 統一異常處理
- 適用場景:批量管理相關任務線程、隔離不同模塊線程、統一異常捕獲
二、線程間通信:wait/notify/notifyAll 機制
1. 核心規則
- 必須在同步塊/方法中調用,否則拋
IllegalMonitorStateException
wait() 釋放鎖並阻塞,直到 notify()/notifyAll() 或中斷
notify() 隨機喚醒一個等待線程;notifyAll() 喚醒所有
- 喚醒後需重新獲取鎖才能繼續執行
2. 經典模式:屏障同步
多線程並行計算,所有線程完成階段性任務後才能進入下一階段(如氣候模擬單元交換邊界數據)。
public class BarrierSync {
private final int totalThreads;
private int arrived = 0;
public BarrierSync(int total) { this.totalThreads = total; }
public synchronized void await() {
arrived++;
if (arrived < totalThreads) {
try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
} else {
arrived = 0;
notifyAll(); // 釋放所有線程
}
}
}
- 最後到達的線程負責
notifyAll()
- 適用於迭代式並行算法、MapReduce 中間同步
3. 現代替代:CyclicBarrier / CountDownLatch (JUC)
// CyclicBarrier 可重用,支持 barrierAction
CyclicBarrier barrier = new CyclicBarrier(threads, () -> System.out.println("All arrived"));
// 每個線程調用 barrier.await()
三、yield():主動讓出 CPU 時間片
Thread.yield() 提示調度器:當前線程願意讓出 CPU,但不保證立即切換
- 適用場景:釋放稀缺資源(數據庫連接、網絡端口)後,希望其他等待該資源的線程優先運行
- 濫用會導致調度開銷增加,謹慎使用
四、守護線程:JVM 退出的關鍵判斷
| 特性 | 用戶線程 | 守護線程 |
| 創建方式 | 默認 | thread.setDaemon(true)(必須在 start() 前) |
| 典型角色 | 業務邏輯、用戶交互 | GC、JIT 編譯、監控、心跳、資源清理 |
| JVM 退出條件 | 所有用戶線程結束 → JVM 退出,強制終止所有守護線程 | 不阻止 JVM 退出 |
陷阱:守護線程中 finally 塊可能不執行(JVM 突然退出),不可在守護線程中執行關鍵持久化邏輯。
五、棄用方法風險:stop/suspend/resume 為何被廢棄
stop():強制釋放所有鎖,導致對象處於不一致狀態,數據損壞風險極高
suspend():不釋放鎖,易導致死鎖(持有鎖的線程被掛起,其他線程永久等待)
resume():配合 suspend 使用,同樣死鎖隱患
- 替代方案:共享
volatile boolean stopFlag + interrupt() 協作式停止
六、多線程調試四大經典難題
1. 死鎖
- 條件:互斥、持有並等待、不可搶佔、循環等待
- 排查:
jstack / VisualVM 線程快照分析 BLOCKED 狀態
- 預防:**全局統一鎖獲取順序**(如按對象 hashCode 排序)、使用
ReentrantLock.tryLock(timeout) 避免無限等待
2. 活鎖
- 線程忙於響應彼此狀態變化而無實質進展(如雙向禮讓走廊)
- 表現:CPU 高佔用、業務指標停滯
- 解決:引入隨機退避、優先級區分、狀態機確保單向推進
3. 內存損壞(數據競爭)
- 非原子複合操作(如
i++、map.put())並發導致數據不一致
- 方案:
synchronized、volatile、AtomicInteger、ConcurrentHashMap 等線程安全類
4. 資源耗盡
- 線程數過多導致文件描述符、數據庫連接、Socket 耗盡
- 方案:線程池 (
ExecutorService) + 連接池 (HikariCP/Druid) + 信號量 (Semaphore) 限流
七、調試工具箱
jstack -l pid:生成線程快照,定位死鎖/阻塞
VisualVM / JConsole:圖形化監控線程狀態、堆內存、CPU
ThreadMXBean:程序化獲取線程信息、死鎖檢測
- 日誌記錄線程名、鎖對象、等待時長,事後重現
八、結語
高級多線程技術的核心在於協作協議的精確設計:屏障同步解決階段推進、守護線程界定生命周期邊界、棄用方法警示強制性中斷的危害、死鎖預防依賴全局有序鎖策略。現代開發應優先使用 JUC (java.util.concurrent) 提供的高級組件(CyclicBarrier, Semaphore, CompletableFuture 等),將底層同步細節交給專家實現,專注於業務並發模型的正確性驗證。
- 上一篇文章: Java 回調機制實現模式:利用接口替代函數指針實現事件驅動
- 下一篇文章: Java 語言核心特性全景解讀:從簡單性、面向對象到架構中立與垃圾回收
Copyright ©
程式碼之家 All Rights Reserved