Java多线程异常需在线程内try-catch捕获或设UncaughtExceptionHandler兜底;推荐Callable+Future方式,通过Future.get()获取ExecutionException并调用getCause()获取原始异常。

Java多线程中,异常不能像单线程那样简单用 try-catch 包裹 main 方法就一劳永逸——子线程抛出的未捕获异常会静默终止,主线程甚至毫无感知。关键在于:每个线程需独立处理自身异常,或统一设置未捕获异常处理器。
子线程内主动捕获异常
最直接的方式是在 run() 方法内部用 try-catch 捕获可能抛出的异常,尤其适用于明确知道哪里可能出错的场景(比如 I/O、解析、网络调用)。
- 把业务逻辑包裹在 try 块中,catch 处理具体异常(如 IOException、NumberFormatException)
- 不要只 catch Exception 或 Throwable,避免掩盖编程错误
- 建议记录日志(如使用 slf4j),必要时向外部传递信号(如设置标志位、通知 CountDownLatch、抛给主线程)
为线程设置 UncaughtExceptionHandler
当子线程未显式捕获异常时,JVM 会调用其关联的 UncaughtExceptionHandler。这是兜底方案,适合统一日志、监控或资源清理。
- 可通过 Thread.setUncaughtExceptionHandler() 为单个线程设置处理器
- 也可通过 Thread.setDefaultUncaughtExceptionHandler() 设置全局默认处理器(对所有未单独设置的线程生效)
- 处理器实现是函数式接口:void uncaughtException(Thread t, Throwable e),其中 t 是出问题的线程,e 是异常对象
使用 Callable + Future 捕获执行异常
Runnable 无法返回结果也无法抛出受检异常;改用 Callable 可以自然抛出异常,并通过 Future.get() 主动获取(此时异常被包装为 ExecutionException)。
立即学习“Java免费学习笔记(深入)”;
- submit(Callable) 返回 Future,调用 get() 会阻塞直到任务完成
- 若任务抛异常,get() 抛出 ExecutionException,其 getCause() 即原始异常
- 适合需要结果或必须确保异常被主线程处理的场景(如任务编排、超时控制)
线程池中的异常处理要点
ThreadPoolExecutor 默认丢弃未捕获异常(仅打印栈跟踪到 System.err),实际项目中需干预:
- 重写 beforeExecute / afterExecute 方法,在执行前后做异常监听或清理
- 自定义 ThreadPoolExecutor,覆盖 afterExecute(Runnable r, Throwable t),t 不为空即表示任务执行异常
- 提交 Runnable 时,可包装一层 try-catch;提交 Callable 时,靠 Future.get() 捕获更稳妥
基本上就这些。核心就两条:要么在线程自己体内拦住异常,要么配好“急救员”(UncaughtExceptionHandler);追求可控和反馈,就选 Callable + Future。不复杂但容易忽略——很多线上问题就卡在子线程挂了却没人发现。










