Future.get()必须配合超时使用,裸调无参get()会导致线程永久阻塞;应使用get(long, TimeUnit)并捕获TimeoutException、ExecutionException和InterruptedException;isDone()/isCancelled()仅用于条件判断而非轮询;CompletableFuture是增强替代但不兼容Future,需重构而非强制转换。

Future.get() 会阻塞,必须配合超时使用
调用 Future.get() 不带参数会一直等待任务完成,线程彻底挂起,这是最常见的线程卡死源头。生产环境几乎从不裸用无参 get()。
- 务必使用带超时的重载:
get(long timeout, TimeUnit unit) - 超时后抛出
TimeoutException,需显式捕获并处理(如降级、重试或返回默认值) - 若任务已取消或执行异常,
get()会包装为ExecutionException抛出,原始异常在getCause()中
Future.isDone() 和 Future.isCancelled() 的真实用途
这两个方法不是用来“轮询等结果”的——频繁调用 isDone() 是反模式,既浪费 CPU 又无法保证及时性。它们的核心场景是:在不确定任务状态时做条件判断。
-
isDone()适合用在回调逻辑中(比如配合ExecutorService.invokeAll()后批量检查) -
isCancelled()必须在get()抛出CancellationException前或后校验,因为一旦任务被取消,get()不会返回结果,也不会再抛ExecutionException - 注意:
cancel(true)发送中断信号,但任务是否响应取决于内部实现(比如是否检查Thread.interrupted())
CompletableFuture 比 Future 更实用,但别直接替换
Future 接口本身能力有限:无法链式处理、不能组合多个异步任务、缺乏非阻塞回调。而 CompletableFuture 是它的增强替代,但二者不兼容——你不能把 CompletableFuture 当作 Future 安全转型后调用 get() 就完事。
- 如果已有基于
ExecutorService.submit()返回的Future,不要强行转成CompletableFuture;应改用CompletableFuture.supplyAsync()等原生 API 重构 -
CompletableFuture的join()类似get(),但抛的是运行时异常(CompletionException),无需 try-catch 检查异常类型 - 组合多个异步任务请用
thenCompose()(串行)、thenCombine()(双任务合并)或allOf()(聚合),而不是手动管理多个Future
Futurefuture = executor.submit(() -> { Thread.sleep(1000); return "done"; }); try { String result = future.get(500, TimeUnit.MILLISECONDS); // 超时设为 500ms } catch (TimeoutException e) { System.err.println("Task timed out"); } catch (ExecutionException e) { Throwable cause = e.getCause(); System.err.println("Task failed: " + cause.getClass().getSimpleName()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 }
真正难的不是调 get(),而是决定「等多久」「超时后怎么兜底」「任务失败时要不要重试」「是否允许取消以及取消后资源是否干净释放」——这些都不在 Future 接口里,得靠业务逻辑补全。
立即学习“Java免费学习笔记(深入)”;










