文章目录
CompletableFuture是JDK 1.8引入的实现类,该类实现了Future和CompletionStage两个接口。该类的实例作为一个异步任务,可以在自己异步执行完成之后触发一些其他的异步任务,从而达到异步回调的效果。
CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会进入另一个阶段。一个阶段可以理解为一个子任务,每一个子任务会包装一个Java函数式接口实例,表示该子任务所要执行的操作。
1. 三种实现接口
每个CompletionStage子任务所包装的可以是一个Function、Consumer或者Runnable函数式接口实例。
这三个常用的函数式接口的特点如下:
被包装接口 | 功能描述 |
---|---|
Function | Function接口的特点是:有输入、有输出。包装了Function实例的CompletionStage子任务需要一个输入参数,并会产生一个输出结果到下一步。 |
Runnable | Runnable接口的特点是:无输入、无输出。包装了Runnable实例的CompletionStage子任务既不需要任何输入参数,又不会产生任何输出。 |
Consumer | Consumer接口的特点是:有输入、无输出。包装了Consumer实例的CompletionStage子任务需要一个输入参数,但不会产生任何输出。 |
2. 链式调用:保证链的顺序性与异步性
多个CompletionStage构成了一条任务流水线,一个环节执行完成了可以将结果移交给下一个环节(子任务)。多个CompletionStage子任务之间可以使用链式调用。
下面是一个顺序调用的例子:
使用
CompletionStage
及其方法构建了一个异步任务链,thenApply
用于对前一个阶段的结果进行计算并传递结果,thenAccept
用于消费前一个阶段的结果并执行操作,thenRun
用于执行无输入输出的操作。
oneStage
//被thenApply包装CompletionStage子任务,由输入输出
.thenApply(x -> square(x))
//消耗上游输出,但是没有输出
.thenAccept(y -> System.out.println(y))
//不消耗上一个子任务的输出又不产生结果
.thenRun(() -> System.out.println())
这种链式操作可以方便地将多个异步操作连接起来,同时保证了操作的顺序性和异步性,提高了代码的可维护性和并发性能。
接下来是一个异步调用的例子:
在这个例子中,task2 和 task3 都依赖于 task1 完成后执行,但它们可能并行执行,也就是说,task2 和 task3 的执行顺序是不确定的,它们不一定会按照 thenRunAsync 的顺序执行。
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
System.out.println("Task 1");
});
CompletableFuture<Void> task2 = task1.thenRunAsync(() -> {
System.out.println("Task 2");
});
CompletableFuture<Void> task3 = task1.thenRunAsync(() -> {
System.out.println("Task 3");
});
3. CompletableFuture创建CompletionStage子任务
CompletableFuture定义了一组方法用于创建CompletionStage子任务(或者阶段性任务),基础的方法如下:
//子任务包装一个Runnable实例,并调用ForkJoinPool.commonPool()线程池来执行
public static CompletableFuture<Void> runAsync(Runnable runnable)
//子任务包装一个Runnable实例,并调用指定的executor线程池来执行
public static CompletableFuture<Void