Callable抛出的运行时异常被封装进Future,必须调用get()才以ExecutionException形式抛出,其getCause()可获取原始异常;不调用get()则异常被静默吞掉。

Java中Callable任务抛出的运行时异常,不会直接在调用线程中抛出,而是被封装进Future对象里;必须通过Future.get()显式获取结果时,才会以ExecutionException的形式重新抛出,其getCause()方法可拿到原始异常。
异常被包装进ExecutionException
当Callable执行过程中抛出RuntimeException(如NullPointerException、IllegalArgumentException等),ExecutorService不会中断线程或立即传播异常。它会把该异常捕获并封装为ExecutionException,绑定到对应的Future实例上。
此时Future的状态变为isDone() == true且isCancelled() == false,但尚未触发异常——只有调用get()才会暴露问题。
必须调用get()才能触发异常传递
Future.get()是唯一能“取出”任务结果(或异常)的操作。若任务已结束且发生异常,get()会立即抛出ExecutionException,内部原因就是原始的RuntimeException。
立即学习“Java免费学习笔记(深入)”;
常见写法示例:
Callable<String> task = () -> {
throw new RuntimeException("模拟运行时异常");
};
Future<String> future = executor.submit(task);
try {
String result = future.get(); // ← 这里才真正抛出ExecutionException
} catch (ExecutionException e) {
Throwable cause = e.getCause(); // ← 获取原始异常,即RuntimeException
System.err.println("原始异常: " + cause.getClass().getSimpleName() + ": " + cause.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
注意区分ExecutionException和CancellationException
Future可能因多种原因失败,需准确识别异常类型:
-
ExecutionException:任务执行中抛出了异常(包括RuntimeException); -
CancellationException:任务被取消(future.cancel(true)后调用get()); -
InterruptedException:当前线程在等待get时被中断。
只对ExecutionException调用getCause()才有意义;其他类型没有嵌套异常。
不调用get()就无法感知异常
如果提交任务后仅检查isDone()或isCancelled(),却从不调用get()或get(timeout, unit),那么即使Callable内部崩溃,主线程也完全无感知——异常被静默吞掉,只存在于Future内部。
因此,在使用Callable+Future时,务必确保每个submit都配对一次get(或超时get),否则异常处理形同虚设。


















