futuretask本身实现了runnable接口,可直接传入thread构造函数并调用start()启动;其get()方法阻塞获取结果,具备内存可见性保证和线程安全控制。

不能直接把 FutureTask 塞进 Thread 的构造函数里执行——因为 Thread 只接受 Runnable,而 FutureTask 本身实现了 Runnable,所以它**本身就是可运行的**,可以直接传给 Thread。关键在于:要确保任务被启动(thread.start()),且后续能安全获取结果(调用 get())。
FutureTask 本质是 Runnable + 可获取结果的包装器
FutureTask 是一个“可取消、可查询状态、可获取返回值”的 Runnable。它内部持有一个 Callable,在 run() 被调用时真正执行该 Callable 并缓存结果。这意味着:
- 你可以把它当作普通
Runnable交给Thread、ExecutorService等执行 - 它不是一创建就执行,必须显式调用
run()或由线程调度触发 - 多个线程重复调用
run()不会重复执行Callable(只执行一次)
正确塞进 Thread 的三步操作
下面是最简但完整的做法:
-
创建 Callable:比如
() -> { Thread.sleep(100); return "done"; } -
包装成 FutureTask:用
new FutureTask(callable) -
传给 Thread 并启动:注意不是
new Thread(task).run()(那是同步调用),而是new Thread(task).start()
示例代码:
Callable<string> task = () -> {
Thread.sleep(500);
return "Hello from FutureTask";
};
FutureTask<string> futureTask = new FutureTask(task);
Thread thread = new Thread(futureTask); // ✅ 合法:FutureTask 实现了 Runnable
thread.start(); // ✅ 必须 start(),否则不执行
// 主线程可稍后取结果(会阻塞直到完成)
try {
String result = futureTask.get(); // ✅ 安全:自动同步,保证看到执行结果
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
</string></string>
为什么说“安全”?关键在 get() 的同步语义
FutureTask.get() 不仅返回结果,还隐含内存可见性保证:
- 如果任务已完成,
get()会看到Callable中写入的所有变量(JMM 保证) - 如果任务未完成,
get()阻塞,直到任务线程执行完run()并设置结果 - 即使你提前调用
isDone()或cancel(),FutureTask内部都用 volatile 和锁做了线程安全控制
常见误区与规避方式
- 误:new Thread(task).run() —— 这是在当前线程同步执行,没开新线程
- 误:多次 new Thread(task).start() —— 同一个 FutureTask 只能 start 一次,第二次抛 IllegalThreadStateException
- 误:不检查异常就调用 get() —— Callable 抛出的异常会被封装为 ExecutionException,需 try-catch
- 建议:若需多次执行相同逻辑,应每次 new 一个新的 FutureTask,而不是复用










