前面两篇文章简单列举了Java 多线程的一些实际场景,但是这两个例子还是过于浅显,仅仅能应对面试官的提问。在后续文章中我们会基于更实战一些的案例来讨论下并发编程。本篇文章使用了大模型进行伪代码和一些特性枚举的生成,本人基于大模型生成的内容做了内容核实和校准,可以放心阅读。
一、引言
在 Java 的并发编程中,我们经常需要在子线程中执行任务并异步获取结果。
早期可以通过 Thread 或 Runnable 来实现并发执行,但它们的缺点是:
- 无法方便地获取任务执行结果;
- 无法优雅地处理任务取消、异常、状态管理等。
为了解决这些问题,JDK 1.5 引入了 Future 接口和 FutureTask 类,为异步任务提供了一种标准化的管理方式。
二、Future 与 FutureTask 简介
1. Future 接口
Future<V> 表示一个异步计算的结果,它提供了几个核心方法:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
这些方法允许我们:
• 取消任务(cancel)
• 检查任务状态(isDone、isCancelled)
• 阻塞等待结果(get)
- FutureTask 类
FutureTask 是 Future 接口的一个实现类,同时还实现了 Runnable 接口。
也就是说:
一个 FutureTask 既是一个任务(可以被线程或线程池执行),也是一个可获取结果的 Future。
其核心类声明如下:
public class FutureTask<V> implements RunnableFuture<V> {
// RunnableFuture 继承了 Runnable 和 Future
}
因此,FutureTask 既能提交给 Thread 执行,也能提交给 ExecutorService 管理。
三、FutureTask 的内部原理
FutureTask 内部是通过 状态机 + CAS + LockSupport 实现的异步结果控制。
- 状态控制
FutureTask 内部有一个 volatile int state 变量,用于标识任务状态:
| 状态名 | 含义 |
|---|---|
| NEW | 初始状态,任务未开始 |
| COMPLETING | 正在设置结果 |
| NORMAL | 执行成功 |
| EXCEPTIONAL | 执行抛出异常 |
| CANCELLED | 已取消(未执行) |
| INTERRUPTING | 正在中断执行 |
| INTERRUPTED | 已中断执行完毕 |
任务状态通过 CAS 操作原子更新,保证线程安全。
⸻
- 执行流程
当调用 FutureTask.run() 时,主要流程如下:
public void run() {
if (state != NEW || !runner.compareAndSet(null, Thread.currentThread()))
return; // 已执行或其他线程执行中
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result = c.call(); // 执行任务
set(result); // 设置结果
}
} catch (Throwable ex) {
setException(ex); // 设置异常
}
}
核心点:
• 任务执行逻辑由 Callable.call() 实现;
• 执行完成后通过 set(result) 设置结果并唤醒等待的线程;
• 如果抛出异常,则调用 setException(ex)。
- 结果等待机制
当主线程调用 get() 时,如果结果未完成,会进入等待状态:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L); // 阻塞等待
return report(s); // 返回结果或抛异常
}
底层使用 LockSupport.park() 让线程阻塞,直到任务完成时调用 LockSupport.unpark() 唤醒。
四、FutureTask 的使用示例
示例一:直接用 Thread 执行 FutureTask
import java.util.concurrent.*;
public class FutureTaskDemo1 {
public static void main(String[] args) throws Exception {
Callable<Integer> callable = () -> {
System.out.println("计算中...");
Thread.sleep(2000);
return 42;
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("主线程执行其他任务...");
System.out.println("结果: " + futureTask.get());
}
}
输出:
主线程执行其他任务...
计算中...
结果: 42
示例二:结合线程池使用
import java.util.concurrent.*;
public class FutureTaskDemo2 {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
FutureTask<String> futureTask = new FutureTask<>(() -> {
Thread.sleep(1000);
return "任务完成";
});
executor.submit(futureTask);
// 等待结果
System.out.println(futureTask.get());
executor.shutdown();
}
}
示例三:任务取消与状态判断
FutureTask<Integer> future = new FutureTask<>(() -> {
Thread.sleep(3000);
return 10;
});
new Thread(future).start();
Thread.sleep(1000);
boolean cancelled = future.cancel(true); // 尝试取消
System.out.println("是否取消成功:" + cancelled);
System.out.println("任务是否完成:" + future.isDone());
五、FutureTask 与 CompletableFuture 的关系
自 Java 8 起,CompletableFuture 进一步增强了 FutureTask 的能力,支持:
• 链式回调 (thenApply, thenAccept)
• 异步组合 (thenCombine, allOf)
• 非阻塞结果获取
可以认为:
FutureTask 是基础设施级的 Future 实现,而 CompletableFuture 是它的进化版本。
六、总结
| 特性 | FutureTask |
|---|---|
| 核心接口 | 实现 RunnableFuture(= Runnable + Future) |
| 功能 | 异步执行、结果获取、取消、状态控制 |
| 内部机制 | CAS 状态机 + LockSupport |
| 使用方式 | 可直接 new Thread(futureTask).start() 或提交给线程池 |
| 适用场景 | 需要精细控制任务生命周期的异步操作 |