多執行緒能大幅提升效能與資源利用,但天生非確定性導致錯誤難重現、難解決。Java 允許單一執行緒拋出異常不影響其他執行緒,這項特性卻催生了隱蔽殺手——孤兒執行緒錯誤模式。
典型生產者-消費者模型:主執行緒(Server)向隊列投遞訊息,工作者執行緒(Client)取出處理。若 Server 拋出未捕獲異常退出,Client 仍在 while(true) 循環等待永不來的訊息,程式表現為凍結無回應,標準錯誤輸出可能留有堆疊追蹤。
代碼示例中,Server 跑 10 次循環後拋出 RuntimeException,Client 永遠卡在 queue.size() == 0 判斷——這就是「孤兒」名稱由來。
治療:修復崩潰執行緒的根本錯誤(顯而易見但常被忽略)。
預防核心策略:在主執行緒捕獲異常,崩潰前主動通知依賴執行緒
public void run() {
try {
while (counter < 10) { client.queue.addElement(new Integer(counter++)); }
throw new RuntimeException("counter >= 10");
} catch (Exception e) {
client.interruptFlag = true; // 通知 Client 停止等待
throw new RuntimeException(e.toString());
}
}
Client 端配合檢查 interruptFlag 退出循環,並處理隊列殘留元素:
while (!interruptFlag) { if (!queue.isEmpty()) processNextElement(); }
while (!queue.isEmpty()) processNextElement(); // 善後
System.out.flush();
System.exit():主線程崩潰且無關鍵資源時可用;若其他執行緒管理開啟檔案等資源,會導致資源洩漏(甚至殘留隊列元素未處理)Thread.stop():已棄用,強制停止會使資源進入不一致狀態,破壞語言安全模型| 模式 | 孤兒執行緒 |
|---|---|
| 症狀 | 多執行緒程式鎖定,可能伴隨堆疊追蹤輸出 |
| 成因 | 多執行緒等待某執行緒輸入,該執行緒因未捕獲異常退出 |
| 治療/預防 | 主執行緒異常處理中通知依賴執行緒,實現體面關閉 |