CompletableFuture、Future、FutureTask
Future
一、是什么?Future 是 Java 5 引入的接口(java.util.concurrent.Future),代表一个异步计算的结果。它提供了一种检查计算是否完成、等待计算完成以及获取计算结果的方法。
二、解决什么问题
- 阻塞问题:传统同步代码会阻塞主线程等待耗时操作(如网络请求、数据库查询)。
- 结果获取:需要一种机制在耗时操作完成后获取结果,而不阻塞主线程。
- 任务状态跟踪:需要监控异步任务的状态(是否完成、是否取消)。
三、核心方法
java
boolean isDone(); // 检查任务是否完成
V get() throws ...; // 阻塞获取结果(直到完成)
V get(long timeout, TimeUnit unit) throws ...; // 限时阻塞获取
boolean cancel(boolean mayInterrupt); // 尝试取消任务四、应用场景
- 提交任务到线程池后获取结果
- 并行执行多个独立任务(每个任务一个 Future)
- 设置任务超时时间(避免无限等待)
五、重要注意事项
get()方法会阻塞调用线程,可能影响性能。- 无法手动设置结果或组合多个任务(如“任务 A 完成后触发任务 B”)。
- 异常处理需在任务内部捕获,无法通过 Future 链式处理。
FutureTask
一、是什么?FutureTask 是 Future 接口的**基础实现类**(java.util.concurrent.FutureTask),同时实现了 Runnable接口。它既可作为Future使用,也可作为Runnable` 提交给线程执行。
二、解决什么问题
- 统一任务封装:将
Callable/Runnable任务封装成Runnable+Future的结合体。 - 直接线程执行:可直接被
Thread或线程池执行(因实现了Runnable)。 - 复用任务对象:同一个任务可被多次执行(需手动重置状态)。
三、核心方法
java
FutureTask(Callable<V> callable); // 构造方法(接受Callable)
FutureTask(Runnable runnable, V result); // 构造方法(接受Runnable)四、应用场景
- 需要手动创建任务并交给线程执行(而非线程池)
- 将已有
Callable或Runnable包装成Future - 需要复用任务对象(通过重置状态)
五、重要注意事项
- 任务一旦完成,状态不可重置(除非调用
runAndReset())。 - 和
Future一样,get()会阻塞线程。 - 无法组合多个任务或链式处理结果。
CompletableFuture
一、是什么?CompletableFuture 是 Java 8 引入的类(java.util.concurrent.CompletableFuture),实现了 Future 接口。它支持非阻塞编程、链式调用和组合异步任务,是函数式异步编程的核心工具。
二、解决什么问题
- 阻塞获取结果:通过回调(如
thenApply())避免get()阻塞。 - 任务组合:轻松实现“任务 A 完成后触发任务 B”或“所有任务完成后合并结果”。
- 异常处理:链式处理异常(
exceptionally()/handle())。 - 手动控制结果:可主动完成任务(
complete()/completeExceptionally())。
三、核心方法
java
static CompletableFuture<Void> runAsync(Runnable runnable); // 无返回任务
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier); // 有返回任务
// 链式处理
thenApply(Function) // 转换结果
thenAccept(Consumer) // 消费结果
thenRun(Runnable) // 无结果操作
// 组合任务
thenCompose() // 串行组合(A 完成后触发 B)
thenCombine() // 并行组合(A+B 完成后合并结果)
allOf() / anyOf() // 聚合多个任务
// 异常处理
exceptionally() // 捕获异常并返回默认值
handle() // 无论成功/异常都处理四、应用场景
- 异步流水线处理(如:请求 API → 解析数据 → 保存数据库)
- 合并多个独立异步任务的结果(如:并发查询多个服务)
- 实现超时控制(
orTimeout()) - 响应式编程(如结合 Spring WebFlux)
五、Java 示例
java
CompletableFuture.supplyAsync(() -> fetchUserFromDB(userId)) // 异步查询
.thenApply(user -> enrichUser(user)) // 转换结果
.thenAccept(user -> saveToCache(user)) // 消费结果
.exceptionally(ex -> { // 异常处理
log.error("Failed", ex);
return null;
})六、重要注意事项
- 默认使用
ForkJoinPool.commonPool(),可指定自定义线程池避免资源竞争。 - 避免在回调中阻塞(如调用
get()),否则失去非阻塞优势。 - 合理处理异常,避免静默失败。
三者核心区别
| 特性 | Future | Task | CompletableFuture |
|---|---|---|---|
| 是否阻塞 | 是(get() 阻塞) | 是 | 否(支持回调) |
| 任务组合能力 | ❌ | ❌ | ✅(thenCompose/allOf 等) |
| 手动设置结果 | ❌ | ❌ | ✅(complete()) |
| 异常处理 | 需在任务内部处理 | 需在任务内部处理 | ✅(链式处理) |
| 函数式支持 | ❌ | ❌ | ✅(Lambda/方法引用) |
| Java 版本 | 5+ | 5+ | 8+ |
总结
- Future:基础接口,适用于简单异步任务获取结果,但需阻塞等待。
- FutureTask:
Future的基础实现,可包装任务并直接交给线程执行。 - CompletableFuture:现代异步编程首选,支持非阻塞、链式调用和任务组合,能优雅处理复杂异步流程。
建议:
- 简单场景用
Future+ 线程池。- 复杂异步流水线、任务组合用
CompletableFuture。- 除非特殊需求(如自定义任务状态),否则无需直接使用
FutureTask。