Java并发包(JUC)FutureTask深度解析
一、核心特性与定位
1.1 双重角色设计
FutureTask是Java并发包中实现RunnableFuture
接口的核心类,同时扮演两种角色:
- Runnable实现:可被Executor线程池执行
- Future实现:提供任务结果获取和状态管理
类继承体系:
1.2 核心特性矩阵
特性 | 行为表现 | 适用场景 |
---|---|---|
状态机驱动 | NEW/COMPLETING/NORMAL/EXCEPTIONAL/CANCELLED | 任务生命周期管理 |
结果缓存 | 计算结果仅保留一次 | 重复获取结果场景 |
中断协作 | 响应Thread.interrupt() | 需要可中断任务的场景 |
线程安全 | 状态变更通过CAS保证 | 多线程环境下的任务控制 |
二、核心机制解析
2.0. 类结构
public class FutureTask<V> implements RunnableFuture<V> {
// 内部状态定义
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
// 任务结果或异常
private Object outcome;
// 调用者(执行任务的线程)
private volatile Thread runner;
// 等待线程队列(通过 AQS 实现)
private volatile WaitNode waiters;
// 构造器(封装 Callable)
public FutureTask(Callable<V> callable) {
this.callable = callable;
this.state = NEW;
}
// 构造器(封装 Runnable 和结果)
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
// 核心方法:执行任务
public void run() {
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 设置异常
}
if (ran)
set(result); // 设置结果
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 核心方法:获取结果(阻塞)
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L); // 等待完成
return report(s);
}
// 核心方法:取消任务
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
if (mayInterruptIfRunning) {
Thread r = runner;
if (r != null)
r.interrupt(); // 中断执行线程
}
} finally {
finishCompletion(); // 唤醒等待线程
}
return true;
}
}
2.0.1. 关键方法详解
-
run()
:- 执行任务逻辑,调用
Callable.call()
。 - 通过
CAS
确保只有一个线程执行任务。 - 任务完成后调用
set(result)
或setException(ex)
更新状态。
- 执行任务逻辑,调用
-
get()
:- 阻塞等待任务完成,通过
awaitDone
进入等待队列。 - 任务完成后调用
report(s)
返回结果或异常。
- 阻塞等待任务完成,通过
-
set(result)
:- 通过
CAS
将状态从COMPLETING
更新为NORMAL
。 - 唤醒所有等待线程(
finishCompletion
)。
- 通过
-
setException(ex)
:- 将状态更新为
EXCEPTIONAL
,并保存异常。 - 唤醒所有等待线程。
- 将状态更新为
2.1 状态转换图
2.2 任务执行流程(源码解析)
// 简化版run()方法逻辑
public void run() {
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 执行实际任务
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 设置异常状态
return;
}
if (ran)
set(result); // 设置正常结果
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
2.3 中断机制
协作式中断实现:
protected void done() {
if (interrupted) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
if (mayInterruptIfRunning) {
Thread r = runner;
if (r != null)
r.interrupt(); // 中断执行线程
}
} finally {
finishCompletion();
}
return true;
}
三、典型使用场景
3.1 异步任务处理
基础使用模式:
// 创建任务
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
Thread.sleep(2000);
return 42;
});
// 提交执行
new Thread(futureTask).start();
// 获取结果(非阻塞)
try {
Integer result = futureTask.get(); // 阻塞直到完成
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
3.2 结果获取与异常处理
高级结果处理:
FutureTask<String> task = new FutureTask<>(() -> {
if (new Random().nextBoolean()) {
throw new IOException("Simulated error");
}
return "Success";
});
new Thread(task).start();
try {
// 带超时的结果获取
String result = task.get(5, TimeUnit.SECONDS);
System.out.println("Result: " + result);
} catch (TimeoutException e) {
System.err.println("Operation timed out");
task.cancel(true);
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
System.err.println("I/O Error: " + cause.getMessage());
}
}
3.3 作为Callable包装器
线程池集成:
ExecutorService executor = Executors.newFixedThreadPool(4);
// 包装Callable任务
FutureTask<Double> piTask = new FutureTask<>(() -> {
return Math.PI; // 复杂计算
});
// 提交到线程池
executor.submit(piTask);
// 获取结果
try {
double pi = piTask.get();
System.out.println("π ≈ " + pi);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
四、最佳实践
4.1 任务取消策略
优雅取消模式:
FutureTask<Void> longRunningTask = new FutureTask<>(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行增量工作
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保持中断状态
}
}
return null;
});
// 取消任务
if (!longRunningTask.cancel(true)) {
System.out.println("Task already completed");
}
4.2 超时控制
超时重试机制:
int retries = 3;
while (retries-- > 0) {
try {
return futureTask.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("Timeout, retrying...");
if (!futureTask.cancel(true)) {
futureTask = new FutureTask<>(task); // 创建新任务
executor.execute(futureTask);
}
}
}
4.3 线程池集成优化
批量提交模式:
ExecutorService executor = Executors.newWorkStealingPool();
List<FutureTask<Integer>> tasks = new ArrayList<>();
// 批量创建任务
for (int i = 0; i < 100; i++) {
final int taskId = i;
FutureTask<Integer> task = new FutureTask<>(() -> {
return process(taskId);
});
tasks.add(task);
executor.execute(task);
}
// 收集结果
List<Integer> results = new ArrayList<>();
for (FutureTask<Integer> task : tasks) {
try {
results.add(task.get());
} catch (Exception e) {
// 异常处理
}
}
五、常见问题与解决方案
5.1 结果获取阻塞问题
解决方案:
- 使用
get(long timeout, TimeUnit unit)
设置超时 - 在单独线程中执行get操作
- 使用CompletionService管理结果
5.2 任务取消注意事项
关键点:
- 仅当任务处于NEW状态时可取消
- 中断正在执行的任务需要任务配合
- 取消后应清理相关资源
5.3 异常处理最佳实践
处理流程:
- 通过
ExecutionException.getCause()
获取原始异常 - 区分已检查异常和运行时异常
- 记录异常上下文信息
- 执行补偿操作(如重试、降级)
六、源码关键逻辑
-
状态机转换:
- 通过
CAS
操作保证状态转换的原子性(如NEW
→COMPLETING
→NORMAL
)。
- 通过
-
等待队列:
- 使用
WaitNode
链表管理等待线程,通过UNSAFE.park
和UNSAFE.unpark
实现线程阻塞和唤醒。
- 使用
-
中断处理:
cancel(true)
会中断执行任务的线程,但需任务内部响应中断(如检查Thread.interrupted()
)。
七、总结
FutureTask
通过状态机和 CAS
操作实现了高效的异步任务管理。其源码核心在于:
- 状态管理:通过
state
变量和CAS
操作控制任务生命周期。 - 结果回调:通过
set(result)
和setException(ex)
更新任务结果。 - 线程协调:通过
WaitNode
队列和UNSAFE
方法管理等待线程。
理解其源码有助于在并发编程中灵活控制异步任务,避免资源泄漏和死锁问题。