Continue to create, accelerate growth! This is the first day of my participation in the "Nuggets Daily New Plan · June Update Challenge", click to view the details of the event
1. What is CompletableFuture?
Do you guys have a feeling that while indulging in business development for a lot of time all day, we lack attention to some technical updates and ignore many practical and simple methods. In the past, when we did asynchronous tasks, we were accustomed to using Callable or To implement the Runnable interface, today we will talk about the CompletableFuture class that is different from it.
CompletableFuture has improved the Future interface. Compared with the Callable/Runnable interface, it supports multi-task chain calls, composition, and multi-task concurrent processing. Many times in the design process, we want to directly obtain the result of an asynchronous task and pass it to the next task to continue the subsequent process. At this time, the role of CompletableFuture comes.
- CompletableFuture class diagram
As can be seen from the following class diagram, CompletableFuture implements the Future and CompletionStage interfaces, and Future provides the function of obtaining the task execution result and task execution status. CompletionStage represents the execution stage of a task and provides many methods to support the aggregation function of multiple tasks.
2. Instructions for using the methods of CompletableFuture
2.1 The CompletableFuture class provides several static methods for asynchronous operations:
supplyAsync and runAsync are mainly used to build asynchronous events.
- supplyAsync:带有返回值的异步任务,支持在默认线程池ForkJoinPool.commonPool()中完成异步任务,也可以使用自定义线程池执行异步任务,结果返回一个新的CompletableFuture,返回结果类型U。最终的任务执行结果可通过返回CompletableFuture对象的 get()/join() 方法获取返回值。
// 使用默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {...}
// 使用自定义线程池Executor
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {...}
// ====================================demo华丽分割线============================================
CompletableFuture<String> supplyAsyncFuture = CompletableFuture.supplyAsync(() -> {
log.info("executing supplyAsync task ...");
return "this is supplyAsync";
});
// 进入阻塞获取异步任务结果
log.info(supplyAsyncFuture.get()); // 输出结果:this is supplyAsync
复制代码
- runAsync:不带返回值的异步任务,支持在默认线程池ForkJoinPool.commonPool()中完成异步任务,也可以使用自定义线程池执行异步任务,结果返回一个新的CompletableFuture,返回结果类型为Void,也就是无返回值。
public static CompletableFuture<Void> runAsync(Runnable runnable) {...}
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {...}
// ====================================demo华丽分割线============================================
CompletableFuture<Void> runAsyncFuture = CompletableFuture.runAsync(() -> {
log.info("executing runAsync task ...");
});
runAsyncFuture.get();
复制代码
- allOf:多个CompletableFuture任务并发执行,所有CompletableFuture任务完成时,返回一个新的CompletableFuture对象,其返回值为Void,也就是无返回值。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {...}
// ====================================demo华丽分割线============================================
// allOf,可传递返回值不同类型的future,最终结果按自己设计预期处理即可
CompletableFuture<String> cf11 = CompletableFuture.supplyAsync(() -> {
log.info("executing supplyAsync task cf11 ...");
return "this is supplyAsync";
});
CompletableFuture<String> cf12 = CompletableFuture.supplyAsync(() -> {
log.info("executing supplyAsync task cf12 ...");
return "this is supplyAsync";
});
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(cf11, cf12);
allOfFuture.get();
复制代码
- anyOf:多个CompletableFuture任务并发执行,只要有一个CompletableFuture任务完成时,就会返回一个新的CompletableFuture对象,并返回该CompletableFuture执行完成任务的返回值。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {...}
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf21 = CompletableFuture.supplyAsync(() -> {
log.info("executing supplyAsync task cf21 ...");
return "this is supplyAsync cf21";
});
CompletableFuture<String> cf22 = CompletableFuture.supplyAsync(() -> {
log.info("executing supplyAsync task cf22 ...");
return "this is supplyAsync cf22";
});
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(cf21, cf22);
log.info("{}", anyOfFuture.get()); // 输出结果:this is supplyAsync cf21或cf22
复制代码
2.2 获取异步任务执行结果的方法 get()/join()
join()和get()方法都是CompletableFuture对象基于阻塞的方式来获取异步任务执行结果。
- get方法会抛出显示异常必须捕获处理,任务允许被中断抛出InterruptedException异常,通过带有超时时间的阻塞方式获取异步任务执行结果,超时等待无结果则中断任务抛出TimeoutException异常。
- join方法会抛出未检查异常,与get()方法不同的是join()方法不允许被中断。
// 可中断,可设置超时时间
public T get() throws InterruptedException, ExecutionException {...}
public T get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {...}
/**
* 不可中断
*/
public T join() {...}
复制代码
3.CompletionStage的方法使用说明
CompletionStage表示一个任务的执行阶段,每个任务都会返回一个CompletionStage对象,可以对多个CompletionStage对象进行串行、并行或者聚合的方式来进行下阶段的操作,也就是说实现异步任务的回调功能。CompletionStage总共提供了38个方法来实现多个CompletionStage任务的各种操作, 接下来我们就针对这些方法分类来了解一下。
以下类型均有三种使用方式:
- thenAccept:方法名不带Async的使用主线程同步执行回调函数,不做异步处理
- thenAcceptAsync:方法名带Async,但是无executor参数的,使用默认线程池ForkJoinPool.commonPool异步执行任务
- thenAcceptAsync:方法名带Async,有executor参数的,使用自定义线程池异步执行任务
3.1 纯消费类型
- 依赖单个任务完成(thenAccept):由上一个CompletionStage任务执行完成的结果传递到action进行回调处理,即仅仅消费了上一个CompletionStage任务的返回值,回调处理结果无返回值。
// 不使用线程池,仅依赖当前线程执行,不做异步
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
// 使用默认线程池ForkJoinPool.commonPool执行任务
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
// 使用自定义线程池执行任务
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture.supplyAsync(() -> "this is supplyAsync")
.thenAcceptAsync((result) -> {
log.info("{} thenAcceptAsync", result);
}).join();
// 输出结果:this is supplyAsync thenAcceptAsync
复制代码
- 依赖两个任务都完成(thenAcceptBoth):两个CompletionStage任务并发执行,必须都完成了才执行action回调处理,即仅仅消费了两个CompletionStage任务的返回值,回调处理结果无返回值。
/**
* 额外多了CompletionStage参数表示CompletionStage任务依赖的另一个CompletionStage任务
* action接收两个参数,分别表示两个CompletionStage任务的返回值
*/
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
// 原理同上,使用默认线程池执行异步任务
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
// 原理同上,使用自定义线程池执行异步任务
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf311 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf311");
CompletableFuture<String> cf312 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf312");
cf311.thenAcceptBothAsync(cf312, (r1, r2) -> {
log.info("{} and {}", r1, r2);
}).join();
// 输出结果:this is supplyAsync cf311 and this is supplyAsync cf312
复制代码
- 依赖两个任务中的任何一个完成(acceptEither):两个CompletionStage任务并发执行,只要其中一个先完成了就携带返回值执行action回调处理,即仅仅消费了优先完成的CompletionStage任务的返回值,回调处理结果无返回值。
/**
* 类似thenAcceptBothAsync,只不过acceptEither只需两个任务中的其中一个完成即可回调action
* action中的值为两个任务中先执行完任务的返回值
*/
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,
Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
Consumer<? super T> action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf311 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf311");
CompletableFuture<String> cf312 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf312");
cf311.acceptEitherAsync(cf312, (r) -> {
log.info(r); // 输出结果:this is supplyAsync cf311或cf312
}).join();
复制代码
3.2 有返回值类型
- 依赖单个任务完成(thenApply):由上一个CompletionStage任务执行完成的结果传递到action进行回调处理,即不止消费了上一个CompletaionStage任务的返回值,同时回调处理结果也有返回值
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf32 = CompletableFuture.supplyAsync(() -> "this is supplyAsync")
.thenApplyAsync(result -> result + " and thenApplyAsync");
log.info(cf32.join()); // 输出结果:this is supplyAsync and thenApplyAsync
复制代码
- 依赖两个任务都完成(thenCombine):两个CompletionStage任务并发执行,必须都完成了才执行action回调处理,即不止消费了两个CompletaionStage任务的返回值,同时回调处理结果也有返回值。
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf321 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf321");
CompletableFuture<String> cf322 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf322");
CompletableFuture<String> thenCombineFuture = cf321.thenCombineAsync(cf322, (r1, r2) -> {
return r1 + " and " + r2;
});
log.info(thenCombineFuture.join());
// 输出结果:this is supplyAsync cf321 and this is supplyAsync cf322
复制代码
- 依赖两个任务中的任何一个完成(applyToEither):两个CompletionStage任务并发执行,只要其中一个任务执行完成就会action回调处理,即不止消费了优先完成的CompletionStage的返回值,同时回调处理结果也有返回值。
// 原理同3.1的acceptEither,只不过applyToEither任务执行完成会返回一个带有返回值的CompletionStage
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,
Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,
Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,
Function<? super T, U> fn, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf321 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf321");
CompletableFuture<String> cf322 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf322");
CompletableFuture<String> thenCombineFuture = cf321.applyToEitherAsync(cf322, (r) -> {
return r;
});
log.info(thenCombineFuture.join());
// 输出结果:this is supplyAsync cf321或cf322
复制代码
3.3 不消费也不返回类型
- 依赖单个任务完成(thenRun):单个CompletionStage任务执行完成回调action处理,即执行action回调方法无参数,回调处理结果也无返回值。
// 上一个CompletionStage任务执行完成后直接回调action处理,无返回值
public CompletionStage<Void> thenRun(Runnable action);
// 同上,使用默认线程池执行action处理
public CompletionStage<Void> thenRunAsync(Runnable action);
// 同上,使用自定义线程池执行action处理
public CompletionStage<Void> thenRunAsync(Runnable action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture.runAsync(() -> {
// TODO
}).thenRunAsync(() -> {
log.info("this is thenRunAsync"); // 输出结果:this is thenRunAsync
}).join();
复制代码
- 依赖两个任务都完成(runAfterBoth):两个CompletionStage任务并发执行,必须两个任务都完成才执行action回调处理,即执行action回调方法无参数,回调处理结果也无返回值。
// 原理同3.1的thenAcceptBoth,只不过runAfterBoth的action回调处理不接收参数且任务执行完成无返回值
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
// 同上,使用默认线程池执行action处理
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action);
// 同上,使用自定义线程池执行action处理
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf331 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf331");
CompletableFuture<String> cf332 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf332");
cf331.runAfterBoth(cf332, () -> {
log.info("this is runAfterBoth");
}).join();
// 输出结果:this is runAfterBoth
复制代码
- 依赖两个任务中的任何一个完成(runAfterEither):两个CompletionStage任务并发执行,只需其中任何一个任务完成即可回调action处理,即执行action回调方法无参数,回调处理结果也无返回值。
public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf331 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf331");
CompletableFuture<String> cf332 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf332");
cf331.runAfterEitherAsync(cf332, () -> {
log.info("this is runAfterEitherAsync");
}).join();
// 输出结果:this is runAfterEitherAsync
复制代码
3.4 组合类型
- thenCompose:存在先后关系的两个任务进行串行组合,由第一个CompletionStage任务执行结果作为参数传递给第二个CompletionStage任务,最终返回第二个CompletionStage。
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> supplyFuture = CompletableFuture.supplyAsync(() -> {
return "this is supplyAsync";
});
CompletableFuture<String> thenComposeFuture = supplyFuture.thenComposeAsync((r) -> {
return CompletableFuture.supplyAsync(() -> {
return r + " and this is thenComposeAsync";
});
});
log.info(thenComposeFuture.join());
// 输出结果:this is supplyAsync and this is thenComposeAsync
复制代码
3.5 任务事件类型
CompletionStage接口也支持类似我们常用的try-catch-finally中的finally的作用,无论这个任务的执行结果是正常还是出现异常的情况,都必须要去执行的一个代码块。在CompletionStage接口提供了以下两种接口回调的形式(whenComplete、handle),并支持主线程同步执行同时也支持使用默认线程池,或者使用自定义线程池去异步执行最终的回调处理。例如我们一个事务操作,无论这段代码执行是否成功,我们都必须要去关闭事务。
- 任务完成事件(whenComplete):结果无返回值,若出现异常执行完whenComplete回调处理完成后将中断主线程的运行。
// 1.whenComplete回调函数中Throwable对象不对空代表出现异常,为空则表示无异常
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> whenCompleteFufute = CompletableFuture.supplyAsync(() -> {
int a = 0;
int b = 100 / a;
return "this is supplyAsync normal";
}).whenCompleteAsync((r, th) -> {
if (th != null) {
log.error("this is whenCompleteAsync error");
}
else {
log.info("this is whenCompleteAsync success");
}
});
log.info(whenCompleteFufute.join()); // 输出结果:this is whenCompleteAsync error
复制代码
- 任务完成回调事件(handle):结果有返回值,若出现异常执行完handle回调处理完成后将继续执行主线程的后续操作,不中断主线程运行。
// 2.handle回调函数中Throwable对象不对空代表出现异常,为空则表示无异常
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor);
// ====================================demo华丽分割线============================================
CompletableFuture<String> whenCompleteFufute = CompletableFuture.supplyAsync(() -> {
int a = 0;
int b = 100 / a;
return "this is supplyAsync normal";
}).handleAsync((r, th) -> {
if (th != null) {
return "this is handleAsync error";
}
else {
return "this is handleAsync success";
}
});
log.info(whenCompleteFufute.join());
// 输出结果:this is handleAsync error
log.info("main thread is running");
// 输出结果:main thread is running
复制代码
4.CompletionStage异常处理方法
- exceptionally : As long as it is a program, there will be exceptions, such as a CompletionStage task. If an exception occurs during execution, in order to ensure that the program can process business logic normally under abnormal conditions, we can use exceptionally here to make exception callbacks deal with. When an exception occurs in the CompletionStage task, the callback will be triggered exceptionally. Otherwise, the normal execution of the CompletionStage task will not process the exception callback.
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
// ====================================demo华丽分割线============================================
CompletableFuture<String> exceptionallyFuture = CompletableFuture.supplyAsync(() -> {
int a = 0;
int b = 10 / a; // 除数为0将抛异常
return "this is supplyAsync normal";
}).exceptionally(th -> {
log.error("exception:{}", th.getMessage());
return "this is exceptionally";
});
log.info(exceptionallyFuture.join()); // 输出结果:this is exceptionally
复制代码
Note : The following two situations may be rare in the actual development process, but still have to be reminded here to avoid design defects due to insufficient preparation in the end.
- When whenCompleteAsync and exceptionally are used at the same time, if an exception occurs, because the exceptionally has a return value, the whenCompleteAsync is executed first, and then the exceptionally is executed.
- When handleAsync and exceptionally appear at the same time, because handleAsync already contains all the operations of exceptionally, that is, the handleAsync callback has a return value, and there is a Throwable exception object that can handle exceptions, so when the two appear at the same time, exceptionally will fail.
5. Summary of method types
According to the above methods, we can conclude that these tasks are actually divided into three categories, which are equivalent to realizing the operations of multiple task strings, multiple tasks in parallel, and multiple tasks aggregation through the callback mechanism of CompletionStage. Therefore, CompletableFuture is used for asynchronous tasks. Processing provides a more powerful programming model. Therefore, the CompletableFuture class provided by java8 provides some chain programming compared to the original Future interface, which saves us many complicated steps of asynchronous task callback operations, allowing us code farmers to output products with higher efficiency.