Java 設計哲學:線程是獨立執行單元,其異常應在線程內部解決。
Runnable.run() 未聲明拋出受檢異常 → 必須內部 try-catch 處理RuntimeException、Error)若未捕獲,導致線程終止,主線程完全無感知,無法通過 try-catch 捕獲Thread thread = new Thread(() -> {
throw new RuntimeException("線程內部異常");
});
// 設置異常處理器
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("線程 " + t.getName() + " 崩潰: " + e.getMessage());
// 記錄日誌、上報監控、嘗試恢復等
});
thread.start();
關鍵點:回調方法 uncaughtException(Thread t, Throwable e) 仍在原線程中執行,非主線程。適用於統一異常上報、日誌記錄、線程池任務異常處理。
適用於「主線程需知曉子線程異常但不立即處理」場景:
public class Result {
private final List exceptions = new CopyOnWriteArrayList<>();
private final CountDownLatch latch;
public Result(int taskCount) { this.latch = new CountDownLatch(taskCount); }
public void addException(Throwable e) { exceptions.add(e); }
public void countDown() { latch.countDown(); }
public List getExceptions() throws InterruptedException {
latch.await();
return exceptions;
}
}
// 子線程
new Thread(() -> {
try {
// 業務邏輯
} catch (Exception e) {
result.addException(e);
} finally {
result.countDown();
}
}).start();
// 主線程後續調用
List errors = result.getExceptions();
// 根據異常類型決定重試、告警、降級等策略
注意:此時子線程已終結,主線程僅持有異常快照。
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);
executor.setThreadFactory(r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler((th, e) -> {
log.error("線程池任務異常: {}", th.getName(), e);
// 可觸發告警、指標上報
});
return t;
});
// 提交任務:使用 Future.get() 獲取異常
Future> future = executor.submit(() -> { ... });
try {
future.get(); // 拋出 ExecutionException,cause 為真實異常
} catch (ExecutionException e) {
handle(e.getCause());
}
| 方案 | 適用場景 | 異常獲取時機 | 線程狀態 |
|---|---|---|---|
| run() 內 try-catch | 常規業務,異常可本地處理 | 即時 | 繼續運行 |
| UncaughtExceptionHandler | 統一日誌、監控上報、不可恢復崩潰 | 線程終止前 | 已終止 |
| 異常收集器 | 批量任務、主線程需決策、異步處理 | 主線程主動獲取 | 已終止 |
| Future.get() | 線程池任務、需返回值或異常 | 主線程調用 get() | 已終止 |
run()/call() 入口包裹頂層 try-catch (Throwable)ThreadFactory 注入 UncaughtExceptionHandleruncaughtException 中執行耗時操作,防止影響 JVM 關閉鉤子