一. CompletableFuture的前世今生
在CompletableFuture出现之前我们先了解一下多线程任务场景的痛点, 根据Oracle官方出具的Java文档说明, 创建线程的方式只有两种, 继承Thread类或者实现Runnable接口。但是这两种方法都存在一个缺陷, 就是都没有返回值, 也就是说我们无法得知线程执行结果。
虽然简单场景下已经满足, 但是当我们需要返回值的时候怎么办呢?所以Java1.5以后有了Callable和Future接口来解决此问题,我们可以通过向线程池提交一个Callable来获取一个包含返回值的Future对象, 从此我们的程序就可以更多的方式来异步执行, 但Future获得线程的执行结果调用的get()方法会阻塞主线程,所以这是个非常耗时的操作,不仅如此,有时我们的一些业务场景需要有更强大的异步能力,比如,将两个异步计算合并为一个,这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果,又或者我们需要同时执行多个异步线程,任意一个执行完就可以继续进行下一步操作, 这时CompletableFuture就要登场了.
Tips:
- CompletionStage 接口里面描述串行关系,主要是 thenApply、thenAccept、thenRun 和 thenCompose 这四个系列的接口
- CompletionStage 接口里面描述 AND 汇聚关系,主要是 thenCombine、thenAcceptBoth 和 runAfterBoth 系列的接口
- CompletionStage 接口里面描述 OR 汇聚关系,主要是 applyToEither、acceptEither 和 runAfterEither 系列的接口
- CompletionStage 接口里面描述 Try-Catch关系,CompletionStage exceptionally(fn)
- CompletionStage 接口里面描述 Try-Finally关系,主要有如下几种
- CompletionStage whenComplete(consumer);
- CompletionStage whenCompleteAsync(consumer);
- CompletionStage handle(fn);
- CompletionStage handleAsync(fn);
从图中我们可以看到CompletableFuture实现了Future与CompletionStage两个接口.
Future接口的核心功主要是用来表示异步计算结果,提供了检查计算是否完成,等待其完成,以及检索计算结果的方法.
CompletionStage接口的核心功能是用于线程异步执行中的阶段处理,其中定义了一组接口用于在一个阶段执行结束之后,要么继续执行下一个阶段,要么对结果进行转换产生新的结果等.
二. CompletableFuture Api使用
-
CompletableFuture 的方法中如不指定自定义的线程池,则默认使用的是Jvm 全局的Forkjoinpool.commonPool() 线程池, 这个线程池会被JDK 和我们常用的 SpringFramework / Springboot 等很多框架共用,所以建议指定自定义的线程池去执行服务中的业务操作
-
CompletableFuture的方法中所有以Async结尾的方法都是另起新的线程来执行任务的
1.任务实例化
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
- runAsync()方法无返回值, 可以指定自定义线程池
- supplyAsync()方法存在返回值, 可以指定自定义线程池
2.获取任务结果
public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()
- get() 方法阻塞获取结果, 可指定超时时间, 抛出经过检查的异常, 强制开发者处理异常
- getNow() 方法不会阻塞, 如果线程执行完成则返回结果, 未执行完成或内部出现异常则返回设置的默认值
- join() 方法也是阻塞获取结果, 区别在于抛出未经检查的异常, 不会强制开发者处理异常
3.Complete中间操作
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
接收参数为上层返回值和一个Throwable异常对象
- whenComplete() 方法为同步方法, 由主线程执行任务
- whenCompleteAsync() 方法为异步方法, 可由默认线程池或指定自定义线程池执行任务
4.Handle中间操作
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
接收参数为上层返回值和一个Throwable异常对象, 并可返回新对象
- handle() 方法为同步方法, 由主线程执行
- handleAsync() 方法为异步方法, 可由默认线程池或指定自定义线程池执行任务
complete()系列方法和handle()系列方法, 区别在于complete()的参数为BiConsumer而handle()为BiFunction,类似lambda中相关的函数式接口定义,此处handle()可以对线程内数据操作,转换等
5.Apply中间操作
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
接收参数为上层返回值, 无异常参数
- thenApply() 方法为同步方法, 由主线程执行
- thenApplyAsync() 方法为异步方法, 可由默认线程池或指定自定义线程池执行任务
6.Accept中间操作
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
- thenAccept() 方法为同步方法, 由主线程执行
- thenAcceptAsync() 方法为异步方法, 可由默认线程池或指定自定义线程池执行任务
- thenAcceptBoth() 方法为同步方法, 该方法的作用是,可与新创建的任务线程组合,并接收两个线程的结果后做相关操作
- thenAcceptBothAsync() 方法为异步方法, 作用同上
7.Exceptionally异常捕获操作
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
public CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn);
public CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor);public CompletableFuture<T> exceptionallyCompose(Function<Throwable, ? extends CompletionStage<T>> fn);
public CompletableFuture<T> exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn);
- exceptionally() 方法捕获链路上的异常,并返回默认值
- exceptionallyAsync() 方法同上, 增加支持自定义线程池
- exceptionallyCompose() 和 exceptionallyComposeAsync() 方法支持捕获异常后返回新的 CompletionStage 实例
8.Compose组合操作
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor);
组合操作和apply操作有些类似, 区别在于组合操作接收上层返回的参数,而返回新的CompletionStage实例
- thenCompose() 方法为同步阻塞方法, 由主线程执行
- thenComposeAsync() 方法为异步方法, 支持自定义线程池
9.Combine合并操作
public <U,V> CompletableFuture<V> thenCombine(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor);
}
合并操作是将上层多个结果逐级合并, 并在合并方法中增加处理操作
- thenCombine() 方法为同步阻塞方法, 由主线程执行
- thenCombineAsync() 方法为异步方法, 支持自定义线程池
10.多个任务聚合后操作
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
- allof() 方法可为多个异步任务提供聚合, 所有异步任务全部完成才继续进行后续操作, 注意该方法无返回值
- anyOf() 方法可为多个异步任务提供聚合, 任意一个异步任务完成就继续进行后续操作, 该方法返回Object类型对象
文章以上内容只介绍了大部分常用的Api并做了简单总结和对比, 希望能在工作中对大家有所帮助, 如有错误欢迎大家参与评论指正, 共同探讨.