必须用Callable而非Runnable,因其call()方法有泛型返回值和受检异常支持,是Future能获取结果的前提;submit(Callable)返回Future,需用带超时的get()并捕获三类异常。

直接用 submit(Callable) 提交任务,是获取异步结果最常用、最推荐的方式。它自动完成 Callable → Future 的封装,省去手动创建 FutureTask 的步骤,线程池统一调度,安全又简洁。
为什么必须用 Callable 而不是 Runnable
Runnable 的 run() 方法返回 void,无法提供计算结果;Callable 的 call() 方法声明泛型返回值(如 Callable<integer></integer>),且能抛出受检异常——这是 Future 能“取到东西”的前提。
- 写
Runnable:提交后只能等执行完,但拿不到任何值 - 写
Callable<string></string>:提交后得到Future<string></string>,后续可调get()拿字符串 - 类型必须匹配:Callable 泛型类型,就是 Future 泛型类型,也是
get()返回的类型
submit 后拿到 Future 怎么安全取结果
Future.get() 是唯一出口,但它会阻塞当前线程,直到任务完成或超时。生产环境绝不能裸调 future.get()(无限等待)。
- 必须设超时:
future.get(3, TimeUnit.SECONDS),避免主线程卡死 - 必须捕获三类异常:
-
TimeoutException:任务没在规定时间内完成 -
ExecutionException:任务内部抛了异常(比如IOException),原异常在e.getCause()里 -
InterruptedException:当前线程在等待时被中断,建议恢复中断状态:Thread.currentThread().interrupt()
-
- 可先查状态:
if (future.isDone()) { ... future.get() ... },避免盲目阻塞
完整实战代码片段
以下是一个带超时、异常处理、类型明确的典型用法:
ExecutorService executor = Executors.newFixedThreadPool(2);
Callable<Integer> task = () -> {
Thread.sleep(1500);
return 100 + 200;
};
Future<Integer> future = executor.submit(task); // ✅ 关键:submit 返回 Future
try {
Integer result = future.get(2, TimeUnit.SECONDS); // ⏱️ 设超时
System.out.println("计算结果:" + result); // 输出:300
} catch (TimeoutException e) {
System.err.println("任务超时,可能还在运行");
} catch (ExecutionException e) {
System.err.println("任务执行失败:" + e.getCause().getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("等待被中断");
}
executor.shutdown();
常见踩坑点提醒
这些错误在真实项目中高频出现:
- 把
submit(Runnable)当成能返回值——它返回Future>,get()永远是null - 提交后不检查是否完成就急着
get(),导致线程挂起,尤其在循环里反复调用时更危险 - 忽略
ExecutionException的包裹机制,直接打印e.toString(),看不到真正报错原因 - 用完线程池不调
shutdown()或awaitTermination(),JVM 无法正常退出

















