第一部分:FutureTask
1. 概念
FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 又继承了 Runnable 和 Future 接口。这意味着:
- 它可以被提交给
ExecutorService(线程池)去执行,就像一个Runnable。 - 它可以用来获取异步计算的结果(或检查状态、取消任务),就像一个
Future。
你可以把它看作一个可取消的异步计算任务。
2. 核心方法
FutureTask(Callable<V> callable): 构造函数,传入一个可返回结果的Callable任务。FutureTask(Runnable runnable, V result): 构造函数,传入一个Runnable和一个指定的返回结果。V get(): 获取计算结果,如果计算未完成,会阻塞直到完成。V get(long timeout, TimeUnit unit): 带超时的get。boolean isDone(): 判断任务是否已完成。boolean cancel(boolean mayInterruptIfRunning): 尝试取消任务。
3. 使用步骤
- 创建任务:定义一个
Callable或Runnable。 - 包装任务:用
FutureTask包装它。 - 执行任务:将
FutureTask提交给线程池,或者用Thread启动。 - 获取结果:在需要的地方调用
get()方法获取结果。
4. 代码示例
import java.util.concurrent.*;
public class FutureTaskExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建一个Callable任务
Callable<Integer> expensiveCalculation = () -> {
Thread.sleep(2000); // 模拟耗时操作
return 42; // 返回答案
};
// 2. 用FutureTask包装Callable任务
FutureTask<Integer> futureTask = new FutureTask<>(expensiveCalculation);
// 3. 创建线程池并提交任务
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(futureTask); // 也可以使用 new Thread(futureTask).start()
System.out.println("主线程可以继续做其他事情...");
// 模拟主线程做其他工作
Thread.sleep(1000);
System.out.println("其他事情做完了,去获取结果...");
// 4. 获取异步任务的结果 (这会阻塞,直到计算完成)
Integer result = futureTask.get();
System.out.println("计算结果是: " + result);
// 关闭线程池
executor.shutdown();
}
}
输出:
主线程可以继续做其他事情...
其他事情做完了,去获取结果...
计算结果是: 42
第二部分:CompletableFuture
1. 概念
CompletableFuture 是 Java 8 引入的类,实现了 Future 和 CompletionStage 接口。它解决了 FutureTask 和传统 Future 的诸多痛点:
- 回调机制:无需手动调用
get()阻塞,可以设置回调函数,结果可用时自动触发。 - 组合与链式编程:可以将多个异步任务串联或并联执行(例如,任务A的结果传给任务B,任务C和D同时执行后合并结果)。
- 异常处理:提供了强大的异常处理机制。
- 手动设置结果:你可以通过
complete()方法手动完成一个 Future。
2. 核心方法(分为异步操作和链式操作)
- 启动异步任务:
supplyAsync(Supplier<U> supplier): 执行一个有返回值的异步任务。runAsync(Runnable runnable): 执行一个无返回值的异步任务。
- 链式处理(核心):
thenApply(): 对上一个任务的结果进行同步转换。“A then B”thenAccept(): 消费上一个任务的结果,无返回值。thenRun(): 上一个任务完成后执行一个操作,不关心其结果。thenCompose(): 扁平化连接两个CompletableFuture。thenCombine(): 合并两个独立异步任务的结果。
- 异常处理:
exceptionally(): 相当于 catch,返回一个默认值。handle(): 无论成功还是异常都会执行,可同时处理结果和异常。
3. 代码示例
示例 1:基础使用 (替代 FutureTask)
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureBasic {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 使用默认的 ForkJoinPool 异步执行一个 Supplier
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return 42;
});
System.out.println("主线程可以继续做其他事情...");
// 仍然可以使用 get() 阻塞获取(但不推荐,失去了异步的优势)
Integer result = future.get();
System.out.println("计算结果是: " + result);
}
}
示例 2:链式调用与回调(推荐方式)
import java.util.concurrent.CompletableFuture;
public class CompletableFutureChain {
public static void main(String[] args) {
// 启动一个异步任务
CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始: " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (Exception e) {}
return "Hello";
})
// 对上一步的结果进行转换 (同步操作)
.thenApply(firstResult -> {
System.out.println("任务2开始: " + Thread.currentThread().getName());
return firstResult + " World";
})
// 消费最终结果 (同步操作)
.thenAccept(finalResult -> {
System.out.println("最终结果是: " + finalResult);
})
// 等待所有阶段完成(防止主线程退出导致程序结束)
.join(); // join() 类似 get(),但不抛出受检异常
System.out.println("主线程结束");
}
}
示例 3:组合多个异步任务 (thenCombine)
import java.util.concurrent.CompletableFuture;
public class CompletableFutureCombine {
public static void main(String[] args) {
// 模拟获取用户ID
CompletableFuture<String> getUser = CompletableFuture.supplyAsync(() -> "user123");
// 模拟获取用户偏好
CompletableFuture<String> getPreference = CompletableFuture.supplyAsync(() -> "Preference_A");
// 将两个独立任务的结果合并
CompletableFuture<String> combinedFuture = getUser.thenCombine(getPreference, (userId, preference) -> {
return "为用户 " + userId + " 推荐 " + preference + " 类商品";
});
// 获取合并后的结果
System.out.println(combinedFuture.join());
}
}
// 输出: 为用户 user123 推荐 Preference_A 类商品
示例 4:异常处理
import java.util.concurrent.CompletableFuture;
public class CompletableFutureException {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (true) { // 模拟一个错误
throw new RuntimeException("Oops! Something went wrong!");
}
return "Success";
})
.exceptionally(ex -> { // 异常处理,返回一个默认值
System.out.println("发生异常: " + ex.getMessage());
return "Default Value";
})
.thenAccept(result -> System.out.println("结果是: " + result))
.join();
}
}
// 输出:
// 发生异常: java.lang.RuntimeException: Oops! Something went wrong!
// 结果是: Default Value
总结与对比
| 特性 | FutureTask | CompletableFuture |
|---|---|---|
| 抽象级别 | 低级别,一个可运行的异步任务 | 高级别,一个可组合的异步计算阶段 |
| 结果获取 | 阻塞式 get() | 支持阻塞 get(),更支持非阻塞回调 |
| 任务组合 | 很难,需要手动编码 | 非常强大且简单 (thenCombine, thenCompose等) |
| 异常处理 | 需要在 Callable 内部处理,或通过 get() 抛出 | 链式异常处理 (exceptionally, handle) |
| 线程控制 | 需手动创建线程或线程池 | 默认使用 ForkJoinPool.commonPool(),也可指定自定义线程池 |
| 手动完成 | 不支持 | 支持 (complete, completeExceptionally) |
- 如果你只是需要一个非常简单的、一次性的异步任务,并且不介意用
get()阻塞获取结果,FutureTask是足够的。 - 对于任何新的开发,强烈推荐使用
CompletableFuture。它的链式编程模型让异步代码变得清晰、易于维护,并且能优雅地处理复杂的并发场景。
1747

被折叠的 条评论
为什么被折叠?



