Java并发编程:FutureTask解析与实战
FutureTask概述
FutureTask是Java并发包(java.util.concurrent)中的一个重要类,它实现了Future接口和Runnable接口。这使得FutureTask既可以作为Runnable被线程执行,又可以作为Future获取Callable的计算结果。
核心特点:
- 实现了Future接口,可以获取异步计算结果
- 实现了Runnable接口,可以被线程执行
- 提供检查计算是否完成的方法
- 可以取消任务的执行
- 可以检索任务执行的结果
实现原理
FutureTask内部维护了一个状态机,定义了任务的不同生命周期阶段:
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
状态转换过程通过CAS操作保证线程安全,内部使用Unsafe类实现原子操作。
核心方法解析
构造方法
// 使用Callable创建
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // 初始状态为NEW
}
// 使用Runnable创建,并通过result指定返回值
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
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);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
get()方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
实战应用
1. 基本使用示例
Callable<Integer> callable = () -> {
Thread.sleep(1000);
return 123;
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// 获取结果,会阻塞直到计算完成
Integer result = futureTask.get();
System.out.println("计算结果: " + result);
2. 结合线程池使用
ExecutorService executor = Executors.newFixedThreadPool(3);
List<FutureTask<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final int taskId = i;
FutureTask<Integer> task = new FutureTask<>(() -> {
Thread.sleep(1000);
return taskId * taskId;
});
tasks.add(task);
executor.submit(task);
}
// 获取所有任务结果
for (FutureTask<Integer> task : tasks) {
System.out.println("任务结果: " + task.get());
}
executor.shutdown();
3. 超时控制
FutureTask<String> task = new FutureTask<>(() -> {
Thread.sleep(3000);
return "任务完成";
});
new Thread(task).start();
try {
// 设置2秒超时
String result = task.get(2, TimeUnit.SECONDS);
System.out.println(result);
} catch (TimeoutException e) {
System.out.println("任务执行超时");
task.cancel(true); // 中断正在执行的任务
}
4. 任务取消
FutureTask<Integer> task = new FutureTask<>(() -> {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
Thread.sleep(100);
if (Thread.currentThread().isInterrupted()) {
System.out.println("任务被中断");
return -1;
}
}
return sum;
});
new Thread(task).start();
Thread.sleep(500);
boolean cancelled = task.cancel(true); // 尝试取消任务
System.out.println("任务取消状态: " + cancelled);
高级应用技巧
1. 回调机制实现
public class CallbackFutureTask<V> extends FutureTask<V> {
private Consumer<V> onSuccess;
private Consumer<Exception> onFailure;
public CallbackFutureTask(Callable<V> callable,
Consumer<V> onSuccess,
Consumer<Exception> onFailure) {
super(callable);
this.onSuccess = onSuccess;
this.onFailure = onFailure;
}
@Override
protected void done() {
try {
V result = get();
onSuccess.accept(result);
} catch (InterruptedException | ExecutionException e) {
onFailure.accept(e);
}
}
}
2. 组合多个FutureTask
public static <T, U, V> FutureTask<V> combine(
FutureTask<T> task1,
FutureTask<U> task2,
BiFunction<T, U, V> combiner) {
return new FutureTask<>(() -> {
T result1 = task1.get();
U result2 = task2.get();
return combiner.apply(result1, result2);
});
}
性能优化与注意事项
- 避免重复计算:FutureTask的结果会被缓存,多次调用get()方法不会重复计算
- 异常处理:任务执行过程中的异常会被捕获并封装在ExecutionException中
- 取消策略:cancel(true)会尝试中断任务线程,cancel(false)仅设置取消状态
- 线程安全:所有状态变更都是原子操作,可以安全地在多线程环境下使用
- 资源释放:任务完成后应及时关闭线程池,避免资源泄漏
总结
FutureTask是Java并发编程中非常有用的工具类,它实现了RunnableFuture接口,同时继承了Future和Runnable的特性。这种双重身份使得FutureTask既可以作为Runnable被线程执行,又可以作为Future获取异步计算结果,为开发者提供了灵活的任务执行和结果获取机制。
核心特性
- 异步计算:FutureTask封装了一个可调用的任务(Callable或Runnable),可以在后台线程中执行
- 结果缓存:计算完成后会缓存结果,后续获取时立即返回
- 状态管理:维护任务的生命周期状态(NEW、COMPLETING、NORMAL、EXCEPTIONAL等)
- 取消支持:提供cancel()方法来中断正在执行的任务
典型使用场景
-
耗时计算:将长时间运行的计算任务交给FutureTask处理
FutureTask<Integer> task = new FutureTask<>(() -> { // 复杂计算逻辑 return someExpensiveCalculation(); }); new Thread(task).start(); Integer result = task.get(); // 阻塞获取结果
-
任务编排:结合ExecutorService构建任务依赖关系
ExecutorService executor = Executors.newFixedThreadPool(2); FutureTask<String> task1 = new FutureTask<>(...); FutureTask<String> task2 = new FutureTask<>(...); executor.submit(task1); executor.submit(() -> { String firstResult = task1.get(); // 等待前置任务完成 return process(firstResult); });
-
延迟初始化:实现线程安全的懒加载模式
class LazyInitializer { private FutureTask<ExpensiveObject> futureTask = new FutureTask<>(ExpensiveObject::new); public ExpensiveObject get() throws Exception { futureTask.run(); // 线程安全地只执行一次 return futureTask.get(); } }
最佳实践
-
超时控制:始终使用带超时的get()方法避免永久阻塞
try { result = futureTask.get(5, TimeUnit.SECONDS); } catch (TimeoutException e) { futureTask.cancel(true); // 中断执行线程 }
-
异常处理:妥善处理ExecutionException和CancellationException
-
资源释放:通过finally块确保线程池关闭
-
状态检查:执行前用isDone()判断任务是否已完成
FutureTask使用注意事项及最佳实践
1. 单次执行特性
- 重要限制:每个FutureTask实例只能被执行一次,重复调用
run()
或提交给线程池会抛出IllegalStateException
- 示例场景:
FutureTask<String> task = new FutureTask<>(() -> "result");
executor.submit(task); // 第一次执行正常
executor.submit(task); // 抛出IllegalStateException
2. 取消操作注意事项
- 中断行为:调用
cancel(true)
时,任务能否被立即中断取决于:- 任务代码是否检查中断标志(
Thread.interrupted()
) - 是否处理了
InterruptedException
- 线程池的中断策略配置
- 任务代码是否检查中断标志(
- 最佳实践:
FutureTask<String> task = new FutureTask<>(() -> {
while(!Thread.interrupted()) {
// 可中断的任务逻辑
}
return "interrupted";
});
3. 大规模任务处理建议
- CompletionService优势:
- 内置任务队列管理
- 提供
take()
方法按完成顺序获取结果 - 避免FutureTask手动管理带来的复杂度
- 适用场景:
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<>(executor);
for(int i=0; i<1000; i++) {
ecs.submit(new CallableTask(i));
}
// 按完成顺序处理结果
for(int i=0; i<1000; i++) {
Future<String> future = ecs.take();
process(future.get());
}
4. 现代替代方案
- CompletableFuture优势:
- 链式调用和组合操作
- 更丰富的异常处理机制
- 内置超时控制
- 迁移示例:
// FutureTask方式
FutureTask<String> task = new FutureTask<>(() -> doSomething());
executor.submit(task);
// CompletableFuture方式
CompletableFuture.supplyAsync(() -> doSomething(), executor)
.thenApply(result -> process(result))
.exceptionally(ex -> handleError(ex));
5. 资源管理要点
- 必须确保:
- 线程池在应用结束时正确关闭
- 长时间运行的任务有超时机制
- I/O资源在任务取消或异常时正确释放
- 典型模式:
try(InputStream is = new FileInputStream(file)) {
// 处理I/O
} finally {
if(!task.isDone()) {
task.cancel(true);
}
}