FutureTask 是唯一可直接构造的 Future 实现类,用于手动控制异步任务生命周期;它封装 Callable 或 Runnable,支持状态查询、结果获取与取消,但可靠取消需任务逻辑配合中断检查。

FutureTask 是用来包装可取消的异步计算任务
它本身不是线程,也不是 Executor,而是一个 Runnable 和 Future 的组合实现:既能被提交给线程池执行(因为实现了 Runnable),又能主动查询状态、获取结果、取消任务(因为实现了 Future)。你真正需要它的场景,往往不是“想用 FutureTask”,而是“想手动控制一个异步任务的生命周期”。
什么时候必须用 FutureTask 而不是直接 new Future?
Future 是接口,不能直接实例化;FutureTask 是目前 JDK 中唯一公开可用的、可直接构造的 Future 实现类。常见误用是以为 submit() 返回的 Future 就是 FutureTask——其实不是,那是线程池内部的私有子类(如 ThreadPoolExecutor.FutureTask)。
- 你需要在提交前就持有任务引用,并反复调用
isDone()、cancel(true)、get() - 你想把同一个任务多次提交(
FutureTask可重用,但仅限未启动或已取消状态) - 你要在非线程池环境里手动触发执行,比如
new Thread(task).start() - 你需要继承并重写
done()方法做回调(FutureTask提供了受保护的钩子)
FutureTask 构造参数差异直接影响行为
它有两个常用构造函数,行为差别关键在「是否预设结果」:
-
new FutureTask(Callable:最常用,任务执行后才产生结果,) get()会阻塞直到完成 -
new FutureTask(Runnable, V):把Runnable包装成Future,但结果固定为传入的V(即get()总是立刻返回该值,和任务实际执行无关)
注意:Runnable 版本无法感知任务是否真的执行完毕,isDone() 在 run() 返回后才为 true,但 get() 不等它——这是容易混淆的点。
立即学习“Java免费学习笔记(深入)”;
cancel(true) 不一定中断正在运行的线程
FutureTask.cancel(true) 的本质是调用底层线程的 interrupt(),效果完全取决于任务代码是否响应中断:
- 如果任务里有
Thread.sleep()、BlockingQueue.take()等可中断阻塞调用,会抛出InterruptedException,此时 cancel 成功 - 如果任务纯 CPU 计算且没检查
Thread.interrupted(),cancel(true)只是设了个中断标志,线程继续跑完 -
cancel(false)更弱:只拒绝尚未开始的任务,对运行中任务无影响
FutureTasktask = new FutureTask<>(() -> { for (int i = 0; i < 1000000; i++) { if (Thread.currentThread().isInterrupted()) { System.out.println("收到中断,提前退出"); return "cancelled"; } // 模拟耗时计算 Math.sqrt(i); } return "done"; }); new Thread(task).start(); Thread.sleep(10); task.cancel(true); // 必须任务自己检查中断才能生效
真正可靠的取消,永远依赖任务逻辑配合,FutureTask 只提供信号通道。很多人卡在这里,是因为只调用了 cancel() 却没改任务体内的判断逻辑。










