futuretask 的核心设计是将 callable 转为可被 thread 执行的 runnable,同时保留 get() 获取结果能力;它实现 runnable 以适配线程启动机制,在 run() 中调用 call() 并用 volatile 和同步保障结果可见性与线程安全。

FutureTask 的核心设计意图,就是把“能返回结果的任务”(Callable)变成“能被线程直接执行的东西”(Runnable),同时保留获取结果的能力。它不是“既实现了 Runnable 又包装了 Callable”的并列关系,而是以 Runnable 身份对外暴露执行入口,内部封装 Callable 并负责调用它、管理状态、提供结果。
它为什么必须实现 Runnable?
因为 Java 线程启动机制只认 Runnable:Thread 构造器只接受 Runnable,start() 后只调用 run() 方法。Callable 的 call() 方法根本无法被 Thread 直接调用。FutureTask 实现 Runnable,就是为了重写自己的 run() 方法——在这个方法里,它才真正去执行内部那个 Callable 的 call(),并把返回值或异常存起来。
它怎么包装 Callable 并保证结果可取?
FutureTask 在构造时接收 Callable,并把它保存为私有字段。当它的 run() 被触发后,会:
- 标记任务状态为“运行中”
- 调用 Callable 的
call()方法 - 把返回值存进内部变量(或捕获异常并存为异常对象)
- 更新状态为“已完成”
这样,后续调用 get() 就能安全读到这个结果——FutureTask 内部用 volatile + 锁/ CAS 保证了可见性和线程安全,不用你手动同步。
它如何做到“既能 run 又能 get”?
靠的是接口继承结构:FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 同时 extends Runnable 和 Future。这就意味着:
- 它可以传给
new Thread(...)或executor.execute(...)—— 因为它是 Runnable - 它能调用
get()、isDone()、cancel()—— 因为它是 Future
换句话说,FutureTask 是一个“带结果的 Runnable”,是 Callable 能走进原始线程模型的桥梁。
一个直观类比
可以把 FutureTask 想成一个“带回执的快递盒”:
- Callable 是你要寄的信(有内容、有回音)
- Runnable 是快递员只认的“标准纸箱”(没标签、不查内容)
- FutureTask 就是那个特制纸箱:外面贴着快递单(符合 Runnable 规范,能被派送),里面密封着你的信,还自带一个“已签收”小窗口(get 方法)——等派送完成,你一掀盖就能看到回执内容
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











