组合式异步编程

使用场景

有一定依赖关系的异步任务。

简单介绍

CompletableFuture,它是一个具体的类,实现了两个接口,一个是Future,另一个是CompletionStage。

Future表示异步任务的结果,而CompletionStage的字面意思是完成阶段。

多个CompletionStage可以以流水线的方式组合起来,对于其中一个CompletionStage,它有一个计算任务,但可能需要等待其他一个或多个阶段完成才能开始,它完成后,可能会触发其他阶段开始运行。

CompletionStage提供了大量方法,使用它们,可以方便地响应任务事件,构建任务流水线,实现组合式异步编程。

使用Future,我们只能通过get获取结果,而get可能会需要阻塞等待,而通过CompletionStage,可以注册回调函数,当任务完成或异常结束时自动触发执行。

Future/FutureTask使用Runnable或Callable表示任务,其中Runnable没有返回值,Callable有返回值。

CompletableFuture则使用Runnable和Supplier表示任务,其中Supplier代替了Callable表示有返回值,与Callable的区别是,它不能抛出受检异常,如果会发生异常,可以抛出运行时异常。

CompletableFuture是对Future的增强,但可以响应结果或异常事件,有很多方法构建异步任务流。

使用CompletableFuture,可以简洁自然地表达多个异步任务之间的依赖关系和执行流程,大大简化代码,提高可读性。

基本使用

CompletableFuture有Future的所有函数功能,除此之外还有如下一些功能:

函数

使用说明

supplyAsync

它接受两个参数supplier和executor,使用executor执行supplier表示的任务,返回一个CompletableFuture,调用后,任务被异步执行,这个方法立即返回。

runAsync

与上面类似,只是无需返回结果,即参数中的supplier换成了runnable。

join

等待任务结束,但它不会抛出受检异常。如果任务异常结束了,join会将异常包装为运行时异常CompletionException抛出。

getNow

与join类似,区别是,如果任务还没有结束,getNow不会等待,而是会返回传入的参数valueIfAbsent。

isCompletedExceptionally

判断任务是否是异常结束。

complete

在CompletableFuture中设置任务成功完成。

completeExceptionally

在CompletableFuture中设置任务异常结束,且附上具体异常。

whenComplete

  • 使用Future,我们只能通过get获取结果,而get可能会需要阻塞等待,而通过Com-pletionStage,可以注册回调函数,当任务完成或异常结束时自动触发执行。
  • 参数action表示回调函数,不管前一个阶段是正常结束还是异常结束,它都会被调用,函数类型是BiConsumer,接受两个参数,第一个参数是正常结束时的结果值,第二个参数是异常结束时的异常,BiConsumer没有返回值。
  • whenComplete的返回值还是CompletableFuture,它不会改变原阶段的结果,还可以在其上继续调用其他函数。

handle

同whenComplete类似,区别是handle回调函数是一个BiFunction,也是接受两个参数,一个是正常结果,另一个是异常,但BiFunction有返回值,在handle返回的CompletableFuture中,结果会被BiFunction的返回值替代,即使原来有异常,也会被覆盖。

exceptionally

  • whenComplete和handle都是既响应正常完成也响应异常,如果只对异常感兴趣,可以使用exceptionally。
  • 它注册的回调函数是Function,接受的参数为异常,返回一个值,与handle类似,它也会改变结果。

任务流

使用CompletableFuture,可以方便地构建有多种依赖关系的任务流。

thenRun

  • 在一个阶段正常完成后,执行下一个任务。
  • 在thenRun构建的任务流中,只有前一个阶段没有异常结束,下一个阶段的任务才会执行,如果前一个阶段发生了异常,所有后续阶段都不会运行,结果会被设为相同的异常,调用join会抛出运行时异常CompletionException。
  • thenRun指定的下一个任务类型是Runnable,它不需要前一个阶段的结果作为参数,也没有返回值,所以,在thenRun返回的CompletableFuture中,结果类型为Void,即没有结果。

thenAccept/thenApply

  • 与thenRun功能类似,不同的是可以为下一个任务提供前一个阶段的结果作为参数。
  • thenAccept的任务类型是Consumer,它接受前一个阶段的结果作为参数,没有返回值。
  • thenApply的任务类型是Function,接受前一个阶段的结果作为参数,返回一个新的值,这个值会成为thenApply返回的CompletableFuture的结果值。

thenCompose

与thenApply类似,不同的是这个转换函数fn的返回值类型是CompletionStage,也就是说,它的返回值也是一个阶段。thenCompose与thenApply的区别就如同Stream API中flatMap与map的区别。

runAfterBoth

thenCombine

thenAcceptBoth

  • runAfterBoth对应的任务类型是Runnable。
  • thenCombine对应的任务类型是BiFunction,接受前两个阶段的结果作为参数,返回一个结果。
  • thenAcceptBoth对应的任务类型是BiConsumer,接受前两个阶段的结果作为参数,但不返回结果。
  • 当前阶段和参数指定的另一个阶段other没有依赖关系,并发执行,当两个都执行结束后,开始执行指定的另一个任务。

runAfterEither

applyToEither

acceptEither

上面三个函数要求两个阶段都完成后才执行下一个任务,如果只需要其中任意一个阶段完成,可以使用这三个方法。

allOf

  • 基于多个CompletableFuture构建了一个新的CompletableFuture。
  • 当所有子CompletableFuture都完成时,它才完成,如果有的Completable-Future异常结束了,则新的CompletableFuture的结果也是异常。
  • 不会因为有异常就提前结束,而是会等待所有阶段结束,如果有多个阶段异常结束,新的Com-pletableFuture中保存的异常是最后一个的。
  • 新的CompletableFuture会持有异常结果,但不会保存正常结束的结果,如果需要,可以从每个阶段中获取。

anyOf

  • 当第一个子CompletableFuture完成或异常结束时,它相应地完成或异常结束,结果与第一个结束的子CompletableFuture一样。

CompletableFuture中有很多名称带有run、accept或apply的方法,它们一般与任务的类型相对应:

  • run        ->  Runnable
  • accept  -> Consumer
  • apply    ->  Function

根据任务由谁执行,一般有三类对应方法:

  • 名称不带Async的方法由当前线程或前一个阶段的线程执行
  • 带Async但没有指定Executor的方法由默认Excecutor(Fork-JoinPool.commonPool()或ThreadPerTaskExecutor)执行
  • 带Async且指定Executor参数的方法由指定的Executor执行

#函数式编程##java原理#
27届毕业生-Java知识专辑 文章被收录于专栏

知其然知其所以然,只有掌握了底层原理,借助第一性原理,才可以在日常开发和项目中运用自如,潇洒走江湖。 专为27届毕业生准备,托起您的就业梦。 该专辑会不定时更新,建议27届同学订阅,入职后扎实的基本功可以帮您争取更好的机会和项目。

全部评论
题2:https://www.nowcoder.com/discuss/860135735427371008
点赞 回复 分享
发布于 今天 12:33 上海
题1:https://www.nowcoder.com/discuss/860135125781106688
点赞 回复 分享
发布于 今天 12:33 上海

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务