为什么需要
主要是因为 jdk1.5 引入了 future, 这种异步开发的方式后,实际使用效果并不优雅。
- Future 获取结果的方式很不优雅,还是需要通过阻塞(或者轮训)的方式。
- Future API 没有流式异常处理
- 多个 Future 不能串联在一起组成链式调用
有时候你需要执行一个长时间运行的计算任务,并且当计算任务完成的时候,你需要把它的计算结果发送给另外一个长时间运行的计算任务等等。你会发现你无法使用 Future 创建这样的一个工作流。
正常的逻辑
- 组合多个 Future 的结果。假设你有 10 个不同的 Future,你想并行的运行,然后在它们运行完成后运行一些函数
如何使用
创建对象
以 Async 结尾并且没有指定 Executor 的方法会使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码。
以下我将会省略 Async 的后缀
中间过程
public CompletableFuture thenAcceptBoth(CompletionStage other, BiConsumer action)
public CompletableFuture runAfterBoth(CompletionStage other, Runnable action)
这里出现了 `CompletionStage` , 这个单词的意思是什么呢?
`CompletionStage` 接口表示异步处理的一个 **阶段(stage)** , 也就是说这代表着阶段的组合。
`Bixxx` 感知上也是一个知识点
BiFunction-BinaryFunction
说明:原先的一个参数,变成两个参数。
- 合并(thenCombine)
```Java
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
-
组合(thenCompose)
将上一轮的返回值作为参数,生成一个新的 CompletableFuture
Future 接口
注意这里的参数 ? extends CompletionStage
表示是返回 CompletionStage 的。
有点像 thenApplyBoth、thenCombine
, 但是这里相当于形成一个新的有着依赖链的 CompletionFuture
和 thenApply
很像,这两个方法之间的差异类似于 map()
和 flatMap()
之间的差异。
由于 allOf
返回的是 CompletableFuture<Void>
因此只能用来等待所有 future 完成或者其中一个失败。
如果你用过 Guava
的 Future
s 类,你就会知道它的 Futures
辅助类提供了很多便利方法,用来处理多个 Future
,比如 Futures.allAsList
,但是对于 CompletableFuture
,我们需要一些辅助方法:
- 将
Future
转化成 CompletableFuture
计算结束
获取结果
源码解析
并不是构建好一个树结构后,分析前后执行关系进行处理。而是 →
先异步执行 supplyAsync
然后在执行 thenXX 的时候进行判断。
- 先判断源任务是否完成,
- 如果完成,直接在对应线程执行以来任务(如果是同步,则在当前线程处理,否则在异步线程处理)
- 如果任务没有完成,直接返回,因为等任务完成之后会通过 postComplete 去触发调用依赖任务。