一、前言
本文内容参考:CompletableFuture基本使用,建议阅读原文。
本文在个人理解基础上有部分删改,仅用于个人学习记录,限于个人能力本文可能出现错漏,因此再次建议阅读原文 CompletableFuture基本使用。
CompletableFuture 是在阅读 Dubbo 异步调用时接触到的内容,当时一直对 CompletableFuture 进行了简单的学习,碍于时间原因一直没有记录,趁现在有时间对 CompletableFuture 进行更深入的学习并记录。
二、介绍
借 CompletableFuture基本使用 文章中对 CompletableFuture 的介绍 :
CompletableFuture是什么
- CompletableFuture是Java8中提供的Future的扩展功能,简化异步编程的复杂性;
- 引入函数式编程,通过回调的方式处理计算结果,也提供了转换和组合的方法;
- 它实现了Future和CompletionStage接口;
- 借助CompletionStage的方法可以实现链式调用;
- 一个CompletetableFuture就代表了一个任务,可以用then,when等操作来防止阻塞和轮询isDone的现象出现;
- 它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作;
CompletableFuture 结构如下:
CompletableFuture 实现了 Future 和 CompletionStage 两个接口。其中 CompletionStage 是Java8新增得一个接口,代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段。目前只有CompletableFuture以及其内部类进行了实现 :
-
一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println())
-
一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发
三、方法介绍
下面我们来看 CompletableFuture 提供的方法,图源 : CompletableFuture基本使用
1. 任务创建
-
CompletableFuture#supplyAsync : 创建一个带有返回值的异步任务
// 使用默认的线程池 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return asyncSupplyStage(asyncPool, supplier); } // 使用自定义的线程池 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) { return asyncSupplyStage(screenExecutor(executor), supplier); }
-
CompletableFuture#runAsync :创建一个没有返回值的异步任务
// 使用默认的线程池 public static CompletableFuture<Void> runAsync(Runnable runnable) { return asyncRunStage(asyncPool, runnable); } // 使用自定义的线程池 public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) { return asyncRunStage(screenExecutor(executor), runnable); }
具体使用如下 :
// supplyAsync有返回值
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "start");
// runAsync 无返回值
CompletableFuture.runAsync(() -> System.out.println("start"));
注 :
- CompletableFuture 默认的使用公共的 ForkJoinPool 线程池执行(默认线程数是 CPU 的核数);如果机器是单核的,则默认使用ThreadPerTaskExecutor,该类是一个内部类,每次执行execute都会创建一个新线程。由于我们在项目中无法控制 ForkJoinPool 在其他处的使用,因此使用默认的 ForkJoinPool 线程池可能会造成任务阻塞,此时我们可以使用自定义的线程池,不过需要注意自定义线程池有个队列满了的拒绝策略,如果采用丢弃策略的拒绝策略,并且allOf方法和get方法如果没有设置超时则会无限期的等待下去。
- CompletableFuture 提供的方法有一部分带有 Async 后缀 ,如 CompletableFuture#thenApply 和 CompletableFuture#thenApplyAsync,CompletableFuture#acceptEither 和 CompletableFuture#acceptEitherAsync 等,其区别在于,方法名带有 Async 修饰的其 function 是异步执行,否则为同步执行。
2. 函数回调
某个任务执行完成后执行的动作(即回调方法)可以理解为串行化一个任务接着一个执行。函数回调方法如下:
-
CompletableFuture#thenApply 以及 CompletableFuture#thenApplyAsync:将上一个任务任务的执行结果,交给后面的Function执行,返回一个具有处理结果的Future对象,
// 同步执行 public <U> CompletableFuture<U> thenApply( Function<? super T,? extends U> fn) { return uniApplyStage(null, fn); } // 异步执行,使用默认的线程池 public <U> CompletableFuture<U> thenApplyAsync( Function<? super T,? extends U> fn) { return uniApplyStage(asyncPool, fn); } // 异步执行,使用入参指定的线程池 public <U> CompletableFuture<U> thenApplyAsync( Function<? super T,? extends U> fn, Executor executor) { return uniApplyStage(screenExecutor(executor), fn); }
下面几个方法同样存在 异步执行方法以及重载方法,实现基本相同,所以不再贴出。
-
CompletableFuture#thenAccept 以及 CompletableFuture#thenAcceptAsync:将上一个任务任务的执行结果(即方法返回值)作为入参传递到回调方法中,无返回值
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) { return uniAcceptStage(null, action); }
-
CompletableFuture#thenRun 以及 CompletableFuture#thenRunAsync :无入参无返回值。不关心结果,只对结果执行Action
public CompletableFuture<Void> thenRun(Runnable action) { return uniRunStage(null, action); }
-
CompletableFuture#thenCompose 以及 CompletableFuture#thenComposeAsync :用来连接两个有依赖关系的任务
public <U> CompletableFuture<U> thenCompose( Function<? super T, ? extends CompletionStage<U>> fn) { return uniComposeStage(null, fn); }
下面我们具体看下:
public static void main(String[] args) throws Exception {
// 创建异步任务
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "start");
System.out.println("##############################");
// thenApply : 将 supplyAsync 的处理结果作为入参,并将返回带有处理后结果的 CompletableFuture
final CompletableFuture<String> thenApply = supplyAsync.thenApply(s -> s + " thenApply");
// 输出 start thenApply
System.out.println(thenApply.get());
// thenAccept :将 supplyAsync 的处理结果作为入参,返回的 CompletableFuture 没有处理结果。
// 输出 start thenAccept
final CompletableFuture<Void> thenAccept = supplyAsync.thenAccept(s -> System.out.println(s + " thenAccept"));
// thenRun : 无入参,返回的 CompletableFuture 没有处理结果。
// 输出 thenRun
final CompletableFuture<Void> thenRun = supplyAsync.thenRun(() -> System.out.println("thenRun"));
// thenCompose : 将 supplyAsync 的处理结果作为入参, 内部经过 另一个 CompletableFuture 处理返回。
// 一般来说内部的 CompletableFuture 依赖于外部的 CompletableFuture 的处理结果, 即这里的CompletableFuture.completedFuture(s) 是依赖于 supplyAsync 的
// 输出 start thenCompose
final CompletableFuture<String> thenCompose = supplyAsync.thenCompose(s -> CompletableFuture.completedFuture(s).thenApply(s1 -> s1 + " thenCompose"));
System.out.println(thenCompose.get());
}
3. 函数聚合
函数聚合分为两种情况:
-
AND : 多个 CompletableFuture 任务都执行结束后,才会执行后续阶段。
-
OR :多个 CompletableFuture 任务只要有一个执行结束,就会执行后续阶段。
3.1 AND
这里需要注意:
- 将多个CompletableFuture都执行完成以后,才会执行后阶段。
- 多个CompletableFuture并行执行,相互之间并没有先后依赖顺序。
- 多个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果,返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。
下面我们来看具体的方法举例:
-
CompletableFuture#thenCombine 以及 thenCombineAsync
public <U,V> CompletableFuture<V> thenCombine( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) { return biApplyStage(null, other, fn); }
-
CompletableFuture#thenAcceptBoth 以及 CompletableFuture#thenAcceptBothAsync
public <U> CompletableFuture<Void> thenAcceptBoth( CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) { return biAcceptStage(null, other, action); }
-
CompletableFuture#runAfterBoth 以及 CompletableFuture#runAfterBothAsync
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) { return biRunStage(null, other, action); }
-
CompletableFuture#allOf
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) { return andTree(cfs, 0, cfs.length - 1); }
具体使用如下:
public static void main(String[] args) throws Exception {
CompletableFuture<String> supplyStartAsync = CompletableFuture.supplyAsync(() -> "start");
CompletableFuture<String> supplyEndAsync = CompletableFuture.supplyAsync(() -> "end");
// AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,将执行结果作为返回值返回
final CompletableFuture<String> thenCombine = supplyStartAsync.thenCombine(supplyEndAsync, (s, s2) -> "thenCombine " + s + " " + s2);
// 输出 thenCombine start end
System.out.println(thenCombine.get());
// AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,无返回值
// 输出 thenAcceptBoth start end
final CompletableFuture<Void> thenAcceptBoth = supplyStartAsync.thenAcceptBoth(supplyEndAsync, (s, s2) -> System.out.println("thenAcceptBoth "+ s + " " + s2));
// AND 关系,当 supplyStartAsync 和 supplyEndAsync 都执行完后才会执行下一步,无入参无返回值
// 输出 runAfterBoth
final CompletableFuture<Void> runAfterBoth = supplyStartAsync.runAfterBoth(supplyEndAsync, () -> System.out.println("runAfterBoth"));
// 当存在多个 CompletableFuture 时, 可以通过 allOf 方法 : 当所有的 CompletableFuture 都执行结束后才会往下执行。
CompletableFuture.allOf(supplyStartAsync, supplyEndAsync).join();
}
3.2 OR
这里需要注意:
- 如果最先执行结束的任务发生了异常,则会将异常信息作为作为指定任务的执行结果,如果最先执行结束的任务并没有抛出异常,即使是后续任务出现了异常,也不会影响当前环节的结果作为指定任务的执行结果。
下面我们来看具体的方法实例:
-
CompletableFuture#applyToEither 以及 CompletableFuture#applyToEitherAsync
public <U> CompletableFuture<U> applyToEither( CompletionStage<? extends T> other, Function<? super T, U> fn) { return orApplyStage(null, other, fn); }
-
CompletableFuture#acceptEither 以及 CompletableFuture#acceptEitherAsync
public CompletableFuture<Void> acceptEither( CompletionStage<? extends T> other, Consumer<? super T> action) { return orAcceptStage(null, other, action); }
-
CompletableFuture#runAfterEither 以及 CompletableFuture#runAfterEitherAsync
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) { return orRunStage(null, other, action); }
-
CompletableFuture#anyOf
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) { return orTree(cfs, 0, cfs.length - 1); }
具体使用如下:
public static void main(String[] args) throws Exception {
CompletableFuture<String> supplyStartAsync = CompletableFuture.supplyAsync(() -> "start")
// 这里要使用异步方法,同步方法使用 OR 规则没有意义,因为执行到 OR 规则时已经执行结束了。
.thenApplyAsync(s -> {
// 为了演示 随机睡眠1-10毫秒,确保两个任务有先后顺序
sleep(RandomUtils.nextLong(1, 10));
return s;
});
CompletableFuture<String> supplyEndAsync = CompletableFuture.supplyAsync(() -> "end")
.thenApplyAsync(s -> {
sleep(RandomUtils.nextLong(1, 10));
return s;
});
// supplyStartAsync 和 supplyEndAsync 谁先执行结束用谁的作为 function 入参,因此这里的输出可能是 applyToEither start 或 applyToEither end
final CompletableFuture<String> applyToEither = supplyStartAsync.applyToEither(supplyEndAsync, s -> "applyToEither " + s);
// 输出 applyToEither start 或 applyToEither end
System.out.println(applyToEither.get());
// supplyStartAsync 和 supplyEndAsync 谁先执行结束用谁的作为 function 入参
// 输出 acceptEither start 或 acceptEither end
final CompletableFuture<Void> acceptEither = supplyStartAsync.acceptEither(supplyEndAsync, (s) -> System.out.println("acceptEither " + s));
// supplyStartAsync 和 supplyEndAsync 任意一个执行结束即会执行 Function
// 输出 runAfterEither
final CompletableFuture<Void> runAfterEither = supplyStartAsync.runAfterEither(supplyEndAsync, () -> System.out.println("runAfterEither"));
// 当存在多个 CompletableFuture 时, anyOf 指定的 CompletableFuture 中有一个完成就会往下继续执行
CompletableFuture.anyOf(supplyStartAsync, supplyEndAsync).join();
}
4. 结果处理
-
CompletableFuture#handle 以及 CompletableFuture#handleAsync
public <U> CompletableFuture<U> handle( BiFunction<? super T, Throwable, ? extends U> fn) { return uniHandleStage(null, fn); }
-
CompletableFuture#exceptionally
public CompletableFuture<T> exceptionally( Function<Throwable, ? extends T> fn) { return uniExceptionallyStage(fn); }
-
CompletableFuture#whenComplete 以及 CompletableFuture#whenCompleteAsync
public CompletableFuture<T> whenComplete( BiConsumer<? super T, ? super Throwable> action) { return uniWhenCompleteStage(null, action); }
具体使用如下:
public static void main(String[] args) throws Exception {
final String result = CompletableFuture.supplyAsync(() -> "end")
// 1. 抛出异常
.thenApplyAsync(s -> {
if (StringUtils.isNotBlank(s)) {
throw new RuntimeException(s);
}
return s;
})
// 2. 前面的任务执行结束后调用,我们这里前一步抛出了异常,所以这里 S 为空, throwable有值
.whenComplete(new BiConsumer<String, Throwable>() {
// s : 正常执行的结果
// throwable : 非正常执行抛出的异常
@Override
public void accept(String s, Throwable throwable) {
System.out.println("whenComplete s = " + s);
System.out.println("whenComplete throwable = " + throwable);
}
})
// 异常处理,前面的步骤有异常抛出时才会执行,这里可以对异常进行处理并返回一个新的结果。
.exceptionally(new Function<Throwable, String>() {
@Override
public String apply(Throwable throwable) {
System.out.println("exceptionally throwable = " + throwable);
return "throwable";
}
})
// 对 前一步的结果做处理
.thenApply(s -> s + " theApply")
// 处理结果,这里 s 已经在 exceptionally 被转换,所以 s 不为空,throwable 为空
.handle(new BiFunction<String, Throwable, String>() {
// s : 正常执行的结果
// throwable : 非正常执行抛出的异常
@Override
public String apply(String s, Throwable throwable) {
System.out.println("handle s = " + s);
System.out.println("handle throwable = " + throwable);
return s;
}
}).join();
// 最终输出 :throwable theApply
System.out.println(result);
}
综上输出内容如下:
whenComplete s = null
whenComplete throwable = java.util.concurrent.CompletionException: java.lang.RuntimeException: end
exceptionally throwable = java.util.concurrent.CompletionException: java.lang.RuntimeException: end
handle s = throwable theApply
handle throwable = null
throwable theApply
5. 结果获取
结果获取方法有下面三种,其使用比较简单,这里不再举例。
- CompletableFuture#get : 如有必要,等待此未来完成,然后返回其结果。
- CompletableFuture#getNow : 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
- CompletableFuture#join : 完成时返回结果值,如果异常完成则抛出(未经检查的)异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture 所涉及的计算引发异常,则此方法将引发(未经检查的) CompletionException ,并将底层异常作为其原因。
本文仅仅做个简单使用的介绍,在Java基础 : CompletableFuture② 代码浅析中对CompletableFuture 的实现做力所能及的学习。
以上:内容部分参考
https://blog.csdn.net/ChenShiAi/article/details/123913374
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正