Runnable 的 run() 方法不可抛出受检异常,需用 try-catch 转为带业务信息的 RuntimeException;或改用 Callable+Future 主动传播异常;也可在 run() 内闭环处理可恢复异常;UncaughtExceptionHandler 仅作运行时异常兜底。

Runnable 接口的 run() 方法签名固定为 public void run(),既不能声明抛出受检异常(checked exception),也不能改变返回值。所以当你在 run() 里调用可能抛出 IOException、SQLException 等受检异常的方法时,必须在方法内部消化掉这些异常——不是“忽略”,而是有策略地转化或处理。
在 run() 中用 try-catch 包装并转为 RuntimeException
这是最常用也最直接的方式。捕获受检异常后,用带 cause 的 RuntimeException(或自定义运行时异常)重新抛出,保留原始堆栈,避免编译报错,同时不丢失错误上下文。
- 写法示例:
catch (IOException e) { throw new RuntimeException("配置加载失败", e); } - 不要只写
throw new RuntimeException(e)—— 缺少业务提示信息,日志里难定位 - 避免空 catch 块:捕获却不记录、不重试、不通知,等于埋雷
改用 Callable + Future 主动传播异常
如果任务本就有结果要返回,或你希望异常能被调用方明确感知和处理,Callable 是更自然的选择。它的 call() 方法允许 throws 任意异常,ExecutorService 会把异常封装进 ExecutionException,通过 future.get() 暴露出来。
Future<String> f = executor.submit(() -> Files.readString(Paths.get("a.txt")));- 调用
f.get()时若发生IOException,会以ExecutionException.getCause()形式返回 - 适合需要结果、需统一错误处理、或对接熔断/重试机制的场景
在 run() 内部完成可恢复的异常处理
有些受检异常属于预期内的临时问题,比如网络超时、文件暂不可读。这时不应往外抛,而应在 run() 里做闭环处理:
立即学习“Java免费学习笔记(深入)”;
- 记录带上下文的日志(如 traceId、参数摘要)
- 执行降级逻辑:返回默认值、重试一次、切换备用数据源
- 必要时更新共享状态(如用
AtomicBoolean标记失败)、发告警消息
设置 UncaughtExceptionHandler 作为兜底
它无法替代上述任一方式,但能捕获漏网的运行时异常或错误(如 NullPointerException、OutOfMemoryError),防止线程静默退出。
- 通过自定义
ThreadFactory为每个线程设置处理器 - 处理器中建议做三件事:打 ERROR 日志、触发实时告警、上报监控指标
- 注意:Callable 的异常不会走这里,因为已被 Future 捕获



















