future.get() 默认阻塞,易卡住,须设超时;callable 才能返回结果,runnable 的 get() 恒为 null;批量任务用 invokeall() 或 completablefuture.allof();future 必须显式持有,否则结果可能被 gc 回收。

Future.get() 为什么经常卡住?
因为 get() 默认阻塞等待结果,线程没结束就一直挂起。常见于任务耗时长、未设超时、或任务本身抛异常没被捕获。
- 务必搭配超时参数使用:
future.get(5, TimeUnit.SECONDS),避免无限等待 - 捕获
ExecutionException(包装了任务内抛出的异常)和TimeoutException,别只写Exception - 如果任务已取消,
get()会直接抛CancellationException,需单独处理 - 注意:调用
get()前最好先用isDone()或isCancelled()做轻量判断,减少不必要的阻塞尝试
Callable 和 Runnable 在 submit() 里怎么选?
关键看要不要返回值。用 Runnable 提交的任务,submit() 返回的 Future> 的 get() 永远是 null;而 Callable 才能真正传递计算结果。
-
ExecutorService.submit(Runnable)→ 返回Future>,get()只能确认是否完成,无法获取业务数据 -
ExecutorService.submit(Callable<t>)</t>→ 返回Future<t></t>,get()返回你call()方法里 return 的值 - 别为了“看起来像有返回”硬套
Runnable+ 成员变量收集结果——这破坏线程安全,且无法通过Future统一管理生命周期
批量提交 Callable 任务后,怎么安全等全部结果?
别用循环调 get() 等前一个再启下一个——这等于串行执行。要用 invokeAll() 或异步轮询 + 超时控制。
-
executor.invokeAll(callableList)会阻塞直到所有任务完成(或超时),返回List<future>></future>,每个元素对应一个任务的结果 - 若需更细粒度控制(比如某个失败不影响其余),改用
submit()批量提交,再用CompletableFuture.allOf()(Java 8+)组合,或手动遍历检查isDone() - 注意
invokeAll()的超时是“整体超时”,不是单个任务超时;若某任务卡死,整个调用仍会等到超时才返回 - 返回的
Future列表顺序与提交顺序一致,可直接按索引取结果,无需额外 map 关联
Future 的结果丢了?可能是被 GC 回收了
Future 对象本身不持有任务引用,一旦你没把 Future 实例存住,又没调 get(),JVM 可能在任务完成后就回收它——后续再想取结果就只能得到 CancellationException 或空指针。
- 必须将
Future存为局部变量、成员变量,或放进集合里长期持有,直到你明确不需要结果为止 - 别写这种代码:
executor.submit(() -> doWork()).get();——Future是临时对象,GC 可能介入,行为不可靠 - 如果任务逻辑复杂、可能失败,建议在
call()内部做日志或状态标记,不能只依赖外部Future的存在来判断执行情况









