Future.get() 同步阻塞调用线程,任务异常封装为ExecutionException,需getCause()获取原始异常;必须捕获InterruptedException、CancellationException和ExecutionException三类异常。

在 Java 中,线程池提交 Callable 任务后返回 Future,调用其 get() 方法会**同步阻塞当前线程**,直到任务完成——无论正常返回结果还是抛出异常。关键在于:异常不会被“吞掉”,而是包装为 ExecutionException 在 get() 调用时原样抛出。
Future.get() 如何暴露任务中的原始异常
Future.get() 不会直接抛出任务里 throw 的异常(比如 NullPointerException 或自定义业务异常),而是将其封装进 ExecutionException 的 cause 中。你需要显式调用 getCause() 才能拿到原始异常对象。
- 任务中抛出
new IllegalArgumentException("id is null") -
future.get()抛出ExecutionException -
e.getCause()返回那个IllegalArgumentException
必须捕获的三种典型异常
调用 get() 时需同时处理以下三类异常,缺一不可:
-
InterruptedException:当前线程在等待时被中断(如调用
Thread.interrupt()) -
CancellationException:任务被取消(
future.cancel(true)后调用get()) - ExecutionException:任务执行过程中抛出的任何异常(含运行时异常、检查异常)
推荐的异常处理写法(带超时更安全)
避免无限阻塞,建议使用带超时的 get(long, TimeUnit),并统一处理异常链:
立即学习“Java免费学习笔记(深入)”;
try {
String result = future.get(5, TimeUnit.SECONDS);
System.out.println("计算结果:" + result);
} catch (TimeoutException e) {
System.err.println("任务超时未完成");
future.cancel(true); // 可选:超时后主动中断执行中的任务
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof BusinessException) {
System.err.println("业务异常:" + cause.getMessage());
} else {
System.err.println("任务执行失败:" + cause);
cause.printStackTrace();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
System.err.println("等待结果时被中断");
} catch (CancellationException e) {
System.err.println("任务已被取消");
}
注意 get() 的阻塞特性与线程池协作关系
get() 阻塞的是**调用它的线程**(比如主线程),不是线程池里的工作线程。工作线程在任务 run/call 完成后就立即归还给池,不参与 get 过程。这意味着:
- 即使线程池已 shutdown,只要 future 还没完成,
get()仍可成功返回结果 - 多个线程可对同一个 future 并发调用
get(),它们都会阻塞到任务完成,然后各自拿到相同结果(或异常) - 若任务耗时很长,而你又在 UI 线程或 Netty EventLoop 中调用
get(),会导致严重卡顿——应改用回调或 CompletableFuture


















