是的,future.get() 默认阻塞调用,线程挂起等待任务完成(成功、异常或取消),不超时则一直等待;需用try-catch处理异常,推荐使用带超时的重载避免无限阻塞。

Future.get() 会一直卡住,直到任务完成吗
是的,Future.get() 默认就是阻塞调用:线程会停在这一行,不往下走,直到任务结束(成功、异常或被取消)。这不是“轮询”,也不依赖回调,而是 JVM 线程直接挂起等待通知。
常见错误现象:get() 调用后整个主线程卡死,但后台任务其实早完成了——这往往是因为你用了 ExecutorService 且没调用 shutdown(),导致 JVM 不退出;或者任务本身抛了未捕获异常,而你没处理 ExecutionException。
- 必须用
try-catch包住get(),否则InterruptedException和ExecutionException会直接炸出来 - 如果任务已取消,
get()会立即抛CancellationException - 若任务还在跑,
get()就真等,哪怕等 10 分钟也不会超时自动返回
怎么避免 get() 无限期阻塞
别裸用 get(),加超时是底线。用带参数的重载:future.get(5, TimeUnit.SECONDS)。超时后抛 TimeoutException,你可以决定是重试、降级还是记录告警。
注意单位不是毫秒整数,而是 long timeout, TimeUnit unit,写成 get(5000, TimeUnit.MILLISECONDS) 是对的,但 get(5000)(无参版本)是毫秒,容易混淆。
- 超时值设太短:可能任务本可完成,但被误判失败;建议先压测出 P95 耗时,再上浮 20%
- 超时值设太长:用户请求卡住,下游服务超时雪崩,尤其在 Web 场景里非常危险
- 不要在响应式(如 Spring WebFlux)或 UI 主线程里调用
get(),它会直接拖垮整个事件循环
get() 返回 null 是不是说明任务出错了
不是。get() 返回 null 只代表任务逻辑明确返回了 null(比如 Callable 实现里写了 return null;)。Java 的 Future 不区分“结果为 null”和“计算异常”——异常全包在 ExecutionException 里。
典型陷阱:你看到 get() 返回 null,就以为任务失败,其实它可能成功执行完了,只是业务逻辑返回了空对象。
- 检查
isDone()和isCancelled()再结合返回值判断状态,比单看null可靠 - 如果业务上不允许
null结果,Callable 实现里最好抛异常,而不是返回null -
CompletableFuture更适合这类场景,它支持join()、getNow()、orTimeout()等更细粒度控制
为什么有时候 get() 看似没阻塞,其实是假象
因为任务可能早已执行完毕,get() 拿的是缓存结果——此时它几乎不耗时。但你不能依赖这个行为,因为执行时机由线程池调度、CPU 负载、任务复杂度共同决定。
一个易被忽略的点:如果你在提交任务后立刻调 get(),而任务又特别轻(比如只做一次 Math.sqrt(4)),那大概率“秒返回”,但这不代表 get() 不阻塞,只是运气好。
- 测试阻塞行为,得用
Thread.sleep(1000)这类显式延迟任务 - 在线上环境,GC 暂停、线程争用、IO 等都可能让原本“快”的任务变慢,
get()随之卡住 - 监控要抓
get()的实际耗时,而不是只看平均值;P99 延迟飙升,往往就是某几个get()死等造成的











