future.get()会阻塞调用线程直至任务完成或超时,实际是线程进入waiting状态等待唤醒;错误地在servlet或android主线程调用会导致响应延迟或anr,应优先使用带超时的get()并捕获timeoutexception、cancellationexception和executionexception。

Future.get() 为什么会阻塞主线程
调用 get() 方法时,当前线程会一直停在那儿,直到异步任务完成或超时。这不是“卡死”,而是线程主动进入 WAITING 状态,等待 Future 内部的 CountDownLatch 或 LockSupport.park() 被唤醒。
常见错误现象:在 Servlet 容器或 Android 主线程里直接调用 get(),导致请求响应延迟、ANR 报错。
- 必须搭配
get(long timeout, TimeUnit unit)使用,避免无限等待 - 超时后抛出
TimeoutException,需显式捕获,不能忽略 - 若任务已取消,
get()会抛CancellationException,不是InterruptedException - 任务执行中抛出异常,
get()会包装成ExecutionException,原始异常在.getCause()里
轮询 isDone() 是反模式
用 while(!future.isDone()) { Thread.sleep(10); } 等待结果,看似“非阻塞”,实则浪费 CPU、干扰调度、增加 GC 压力,且无法感知任务失败或取消。
使用场景仅限极少数嵌入式或实时性要求苛刻的轮询协议(如自定义硬件握手),Java 标准库中无正当理由应避免。
立即学习“Java免费学习笔记(深入)”;
-
isDone()只反映状态,不提供通知机制,无法替代回调 - 频繁调用可能触发 JVM 内存屏障开销,尤其在多核高并发下
- 和
get()混用易引发竞态:比如刚判断isDone()==true,紧接着调用get()却抛出CancellationException
CompletableFuture 比 Future 更实用
原生 Future 无法链式处理、无法注册回调、不支持组合操作——这些不是设计缺陷,而是它本就只定位为“结果容器”。真要编排异步逻辑,得用 CompletableFuture。
性能影响很小:它本质是 Future 的增强实现,继承关系为 CompletableFuture extends Future,兼容老接口。
- 用
thenApply()替代手动get()+ 计算,避免阻塞 -
exceptionally()和handle()能统一收口异常,不用层层 try-catch - 多个异步任务并行用
allOf(),任一完成用anyOf(),原生Future做不到 - 注意默认使用
ForkJoinPool.commonPool(),IO 密集型任务建议显式传入自定义线程池
submit(Callable) 和 execute(Runnable) 的返回值差异
ExecutorService.submit() 返回 Future,execute() 不返回任何东西。这不是疏漏,而是语义区别:Callable 表示“有结果要取”,Runnable 表示“只管执行完就行”。
容易踩的坑是误以为 execute() 也能拿结果,或者给 submit() 传 Runnable 却忘了第二个参数 result 会被包装进 Future。
-
submit(Runnable task, T result)中的result是任务完成时get()返回的固定值,不是任务计算结果 -
submit(Runnable task)返回的Future的get()永远返回null,别用来判断是否成功 - 若任务本身无返回值但又需要监控状态,优先用
submit(() -> { ...; return null; }),而非execute()
真正难的是把“等结果”这个动作从流程里摘出来——不是选对 API 就行,得重构调用方的控制流。很多人卡在 get() 那一行,其实问题不在 Future,而在没想清楚:这里到底该同步等,还是该用回调/事件/状态机推进。










