線程 run() 拋出未捕獲的 非受檢異常(RuntimeException 及子類),默認行為:打印堆棧 → 線程終止 → 若為主線程則 JVM 退出。生產環境需:統一日誌、告警、資源清理、不影響其他線程。
public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(ExceptionHandler.class);
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("線程 [{}] 發生未捕獲異常:{}", t.getName(), e.getMessage(), e);
// 可在此:上報監控、釋放資源、嘗試重啟任務等
}
}
// 使用
Thread worker = new Thread(() -> {
int x = Integer.parseInt("abc"); // NumberFormatException
}, "Parser-Thread");
worker.setUncaughtExceptionHandler(new ExceptionHandler());
worker.start();
// 應用啟動早期設置
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
log.error("全局捕獲 - 線程:{},異常:{}", t.getName(), e.toString(), e);
});
thread.setUncaughtExceptionHandler(handler)ThreadGroup.uncaughtException()(線程組可繼承重寫)Thread.setDefaultUncaughtExceptionHandler()痛點:ExecutorService.execute(Runnable) 提交的任務異常不會觸發上述機制,會被吞噬或包裝在 Future.get() 拋出。
ThreadFactory factory = r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new ExceptionHandler());
return t;
};
ExecutorService pool = Executors.newFixedThreadPool(4, factory);
public static Runnable safe(Runnable task, Thread.UncaughtExceptionHandler handler) {
return () -> {
try { task.run(); }
catch (Throwable e) {
Thread t = Thread.currentThread();
handler.uncaughtException(t, e);
}
};
}
// 使用
pool.execute(safe(() -> { risky(); }, new ExceptionHandler()));
Future> f = pool.submit(() -> { risky(); });
try { f.get(); }
catch (ExecutionException e) {
log.error("任務異常", e.getCause());
}
public class ResilientExecutor {
private final ExecutorService pool;
private final Thread.UncaughtExceptionHandler handler;
private final int maxRetries;
public ResilientExecutor(int threads, int maxRetries) {
this.pool = Executors.newFixedThreadPool(threads);
this.handler = (t, e) -> log.error("任務線程 {} 異常", t.getName(), e);
this.maxRetries = maxRetries;
}
public void submitWithRetry(Runnable task) {
pool.execute(() -> {
int attempt = 0;
while (true) {
try {
task.run();
return;
} catch (Throwable e) {
attempt++;
if (attempt > maxRetries) {
handler.uncaughtException(Thread.currentThread(), e);
return;
}
log.warn("任務失敗,第 {} 次重試", attempt);
sleep(1000L * attempt);
}
}
});
}
private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
}