面试知识库
基础

Callable与Future#

一句话答案#

Callable 有返回值可抛异常(vs Runnable),Future 获取异步结果(get 阻塞),FutureTask 是桥梁,局限性推荐 CompletableFuture。

核心要点

Runnable vs Callable:

维度RunnableCallable
返回值voidV
异常不能抛出 checked可以抛出 Exception
方法run()call()

Future 接口:

  • get():阻塞等待结果
  • get(timeout, unit):超时等待
  • isDone():是否完成
  • cancel():取消任务

FutureTask 使用:

Callable<Integer> task = () -> heavyComputation();
FutureTask<Integer> ft = new FutureTask<>(task);
new Thread(ft).start();
Integer result = ft.get(); // 阻塞
java

局限性: get() 阻塞、无法组合多个 Future → 推荐 CompletableFuture

面试回答(2分钟版)

Callable 和 Runnable 都是任务接口,核心区别是 Callable 的 call() 方法有返回值且可以抛出受检异常,而 Runnable 的 run() 无返回值不能抛异常。Future 是异步计算结果的抽象接口,通过 get() 获取结果,get() 会阻塞当前线程直到任务完成,还支持 isDone() 检查状态和 cancel() 取消任务。FutureTask 同时实现了 Runnable 和 Future,是连接 Callable 与线程池的桥梁,可以直接提交给 Thread 或 ExecutorService 执行。但 Future 有明显局限性:get() 是阻塞的会让调用线程空等,多个 Future 之间无法组合和编排,也无法注册完成回调。所以生产中推荐使用 CompletableFuture,它支持 supplyAsync 异步执行、thenApply 链式转换、exceptionally 异常兜底,以及 allOf/anyOf 多任务组合,彻底解决了 Future 的阻塞等待问题。使用 Future.get() 时务必设置超时时间避免永久阻塞,并记得处理 ExecutionException。

追问与易错

追问方向:

  • “Future.get() 阻塞有什么问题?”→ 调用线程空等浪费资源,且无超时控制可能永久阻塞;生产中应使用 get(timeout, unit) 或切换到 CompletableFuture 的非阻塞回调模式
  • “FutureTask 可以被多个线程提交吗?”→ 可以提交多次但只会执行一次,FutureTask 内部用 state 状态机保证 run() 只执行一次,多个线程 get() 都能拿到同一个结果
  • “Callable 和 Runnable 的适用场景?”→ 需要返回值或抛受检异常用 Callable,纯执行无返回用 Runnable;线程池 submit 支持 Callable,execute 只支持 Runnable

易错点:

  • ❌ Future.get() 不设超时——可能永久阻塞
  • ❌ 忘记处理 ExecutionException