單線程應用主線程拋出未捕獲異常,控制檯打印堆棧、程序停止,極易發現。但多線程服務端應用(無控制檯、長期運行)中,工作線程因 RuntimeException/Error 悄然終止,往往不留痕跡,導致局部功能失效、行為詭異,極難排查。
最常見原因:NullPointerException、ArrayIndexOutOfBoundsException 等未捕獲運行時異常。調用第三方插件 incomingResponse() 時極易發生——插件代碼不可控、編寫時間滯後,無法審核正確性。
// 脆弱版本
while (true) {
ir = queue.getNext();
plugIn.handleMessage(ir.getResponse()); // 拋出異常 → 線程死
}
// 健壯版本
while (true) {
ir = queue.getNext();
PlugIn plugIn = findPlugIn(ir.getResponseId());
if (plugIn != null) {
try {
plugIn.handleMessage(ir.getResponse());
} catch (RuntimeException e) {
// 糾正動作三選一:
// 1. 記錄日誌,處理下一條消息(TimerTask 風格)
// 2. 記錄日誌,重啟當前工作線程
// 3. 記錄日誌,卸載違規插件(隔離故障域)
}
}
}
public class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String name) { super(name); }
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Thread {} died: {}", t.getName(), e);
// 可選:重啟線程、告警、甚至關閉系統
}
}
// 使用
static final ThreadGroup WORKERS = new MyThreadGroup("Worker Threads");
new Thread(WORKERS, () -> { /* 任務 */ }, "Worker-1").start();
JDK 1.5+ 推薦使用 Thread.setUncaughtExceptionHandler() 或 ExecutorService 的 ThreadFactory 設置,更靈活。
try-catch (RuntimeException)。UncaughtExceptionHandler,記錄完整堆棧,避免「線程去哪兒了」的迷茫。ThreadPoolExecutor 並重寫 afterExecute() 檢查異常,或提交 Future 統一 get() 捕獲。預防 + 檢測 = 線程洩漏的最佳防線。別讓線程在黑暗中消失。