内容概要
CompletableFuture
类使得并发任务的处理变得简单而高效,通过简洁的API,开发者能轻松创建、组合和链式调用异步操作,无需关心底层线程管理,这不仅提升了程序的响应速度,还优化了资源利用率,让复杂的并发逻辑变得易于掌控。
核心概念
CompletableFuture
是一个非常强大的并发工具类,它实现了 Future
和 CompletionStage
接口,用于表示某个异步计算的结果,与传统的 Future
不同,CompletableFuture
提供了函数式编程的方法,可以更容易地组织异步代码,处理回调和组合多个异步操作。
假设,有一个电商网站,用户浏览产品详情页时,需要展示产品的基本信息、价格、库存、用户评价等多个方面的数据,这些数据可能来自不同的数据源或服务,比如:
- 产品基本信息可能来自一个主数据库。
- 价格 和库存 可能需要实时从另一个库存服务获取。
- 用户评价可能存储在另一个专门用于用户反馈的系统中。
为了提升用户体验,希望这些数据的获取能够并行进行,而不是一个接一个地串行获取,这就是 CompletableFuture
的经典场景。
CompletableFuture
类在主要用来解决异步编程和并发执行的问题,在传统的同步编程模型中,代码的执行通常是阻塞的,即一行代码执行完成后,下一行代码才能开始执行,这种模型在处理耗时操作时,如 I/O 操作、数据库访问或网络请求,会导致线程长时间闲置,等待操作完成,从而降低系统的吞吐量和响应能力。
因此,CompletableFuture
类提供了一种非阻塞的、基于回调的编程方式,可以在等待某个长时间运行的任务完成时,同时执行其他任务,这样,就可以更充分地利用系统资源,提高程序的并发性和响应速度。
使用CompletableFuture
通常用于解决以下类似场景的问题:
- 发起异步请求 :当用户请求一个产品详情页时,后端服务可以同时发起对三个数据源的异步请求,这可以通过创建三个
CompletableFuture
实例来实现,每个实例负责一个数据源的请求。 - 处理异步结果 :一旦这些异步请求发出,它们就可以独立地执行,主线程可以继续处理其他任务,当某个
CompletableFuture
完成时,它会包含一个结果(或者是执行过程中的异常)。 - 组合异步结果 :使用
CompletableFuture
的组合方法(如thenCombine
、thenAcceptBoth
或allOf
),可以等待所有异步操作完成,并将它们的结果组合在一起,比如,可以等待产品基本信息、价格和库存以及用户评价都返回后,再将这些数据整合到一个响应对象中,返回给前端。 - 异常处理 :如果在获取某个数据源时发生异常,
CompletableFuture
允许以异步的方式处理这些异常,比如通过exceptionally
方法提供一个默认的备选结果或执行一些清理操作。 - 最终响应:一旦所有数据源的数据都成功获取并组合在一起,或者某个数据源发生异常并得到了妥善处理,服务就可以将最终的产品详情页响应发送给前端用户。
使用CompletableFuture
可以高效的并发数据获取,提升系统的响应速度和整体性能。
代码案例
当然,以下是一个简单的Java代码示例,演示了如何使用CompletableFuture
类来异步执行任务,并在任务完成后获取结果,这个示例模拟了一个client调用,异步地获取两个不同数据源的数据,并在它们都完成后组合这两个结果,如下代码案例:
java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureDemo {
// 模拟从某个数据源获取数据的耗时操作
private static String fetchDataFromSourceA() {
simulateSlowService();
return "Data from Source A";
}
// 模拟从另一个数据源获取数据的耗时操作
private static String fetchDataFromSourceB() {
simulateSlowService();
return "Data from Source B";
}
// 模拟耗时操作的方法(例如网络延迟)
private static void simulateSlowService() {
try {
Thread.sleep(2000); // 模拟耗时2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 客户端调用示例
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建两个异步任务
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(CompletableFutureDemo::fetchDataFromSourceA);
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(CompletableFutureDemo::fetchDataFromSourceB);
// 当两个任务都完成时,组合它们的结果
CompletableFuture<String> combinedFuture = futureA.thenCombine(futureB, (resultA, resultB) -> {
return resultA + " and " + resultB;
});
// 等待组合任务完成并获取结果
String combinedResult = combinedFuture.get();
// 输出结果
System.out.println("Combined result: " + combinedResult);
}
}
在上面代码中:
fetchDataFromSourceA
和 fetchDataFromSourceB
方法模拟了从两个不同的数据源获取数据的耗时操作,simulateSlowService
方法通过让当前线程休眠2秒来模拟一个耗时操作,在 main
方法中,使用 CompletableFuture.supplyAsync
方法创建了两个异步任务,分别对应两个数据源的数据获取,使用 thenCombine
方法将这两个异步任务的结果组合在一起,当两个任务都完成时,会调用提供的 lambda 表达式来合并结果,combinedFuture.get()
方法会阻塞当前线程,直到组合任务完成并返回结果。
核心API
CompletableFuture
列用于表示某个异步计算的结果,它提供了函数式编程的方法来处理异步计算,允许以非阻塞的方式编写并发代码,并且可以链接多个异步操作,以下是一些常用方法的含义:
1、静态工厂方法
CompletableFuture.supplyAsync(Supplier<? extends U> supplier)
: 异步执行给定的Supplier
,并返回一个表示结果的新CompletableFuture
。CompletableFuture.supplyAsync(Supplier<? extends U> supplier, Executor executor)
: 使用指定的执行器异步执行给定的Supplier
。CompletableFuture.runAsync(Runnable runnable)
: 异步执行给定的Runnable
,并返回一个表示其完成的新CompletableFuture
。CompletableFuture.runAsync(Runnable runnable, Executor executor)
: 使用指定的执行器异步执行给定的Runnable
。
2、完成时的处理
thenApply(Function<? super T,? extends U> fn)
: 当此CompletableFuture
完成时,对其结果应用给定的函数。thenAccept(Consumer<? super T> action)
: 当此CompletableFuture
完成时,执行给定的操作。thenRun(Runnable action)
: 当此CompletableFuture
完成时,执行给定的无参数操作。
3、异常处理
exceptionally(Function<Throwable,? extends T> fn)
: 当此CompletableFuture
异常完成时,对其异常应用给定的函数。
4、组合多个 CompletableFuture
thenCombine(CompletableFuture<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
: 当此CompletableFuture
和另一个都完成时,使用给定的函数组合它们的结果。thenAcceptBoth(CompletableFuture<? extends U> other, BiConsumer<? super T,? super U> action)
: 当此CompletableFuture
和另一个都完成时,对它们的结果执行给定的操作。runAfterBoth(CompletableFuture<?> other, Runnable action)
: 当此CompletableFuture
和另一个都完成时,执行给定的操作。applyToEither(CompletableFuture<? extends T> other, Function<? super T, U> fn)
: 当此CompletableFuture
或另一个完成时(哪个先完成),对其结果应用给定的函数。acceptEither(CompletableFuture<? extends T> other, Consumer<? super T> action)
: 当此CompletableFuture
或另一个完成时(哪个先完成),对其结果执行给定的操作。runAfterEither(CompletableFuture<?> other, Runnable action)
: 当此CompletableFuture
或另一个完成时(哪个先完成),执行给定的操作。
5、等待和获取结果
get()
: 等待计算完成,然后获取其结果。get(long timeout, TimeUnit unit)
: 等待计算在给定的时间内完成,并获取其结果。join()
: 类似于get()
,但是会在计算未完成时抛出未检查的异常。complete(T value)
: 如果尚未完成,则设置此CompletableFuture
的结果。completeExceptionally(Throwable ex)
: 如果尚未完成,则使此CompletableFuture
异常完成。
6、取消
cancel(boolean mayInterruptIfRunning)
: 尝试取消此CompletableFuture
。isCancelled()
: 如果此CompletableFuture
被取消,则返回true
。
7、查询
isDone()
: 如果此CompletableFuture
完成(无论是正常完成还是异常完成),则返回true
。
核心总结
CompletableFuture
类为异步编程提供了强大的支持,使得并发处理变得更加直观和高效,它能够轻松创建、组合和管理异步任务,提升程序的响应性和吞吐量,但是如果遇到错误处理相对复杂,可能需要额外的代码来确保异常被正确处理。对于复杂的异步逻辑,可以尝试将其拆分成小的、易于管理的部分,每个部分使用CompletableFuture
来处理,同时,要注意异常处理和资源管理,避免潜在的内存泄漏和程序崩溃。
END! END! END!
往期回顾
精品文章
Java并发基础:ConcurrentSkipListMap全面解析
Java并发基础:ConcurrentSkipListSet全面解析!
Java并发基础:SynchronousQueue全面解析!
Java并发基础:ConcurrentLinkedQueue全面解析!