2 使用
从类来看,其实现了CompletionStage接口以及Future接口;futrue的用法就不在这里说了,这里仅仅说明CompletionStage方法以及相关方法用法;
调用整个过程,我把它看成是个流,每次方法生成的CompletableFuture都是一个流节点,每个流有自己的完成结果,其后面的流依赖其完成后才可执行
2.1 流的产生
- 静态方法
val ff = CompletableFuture()
- 数据提供者Supplier
CompletableFuture.supplyAsync {
println(“create thread ${Thread.currentThread().name}”)
100
}
- 任务事件Runnable
CompletableFuture.runAsync {
println(“create: buy meat and vegetables”)
}
- 组合并集任务
CompletableFuture.allOf(CompletableFuture.runAsync{
println(“create: wear shoes”)
}, CompletableFuture.runAsync{
println(“create: wear dress”)
})
- 组合互斥任务
CompletableFuture.anyOf(CompletableFuture.runAsync{
println(“create: read a book”)
}, CompletableFuture.runAsync{
println(“create: write”)
})
2.2 流的处理
流的处理方法比较多了,有37个,写代码不方便;完成方法表格如下,表格备注表达了我对这些方法的抽象与理解,看了这个,有助于更好理解下面涉及的东西
太多了很难记,也不好理解,下面给出了简略精华版本方法表;通过这些方法,清除明了这个类可以做到什么样得组合变换
下面给出几个简单事例
- 无组合的变化、消费
CompletableFuture.supplyAsync {
println(“create thread ${Thread.currentThread().name}”)
100
}.thenApply {
println(“map thread ${Thread.currentThread().name}”)
it * 10
}.thenAccept {
println(“consume $it”)
}
- 组合变化、消费
CompletableFuture.supplyAsync {
10
}.applyToEither(CompletableFuture.supplyAsync {
100
}, Function<Int, Int> {
it * 10 + 3
}).thenCombine(CompletableFuture.supplyAsync{
“Lily”
}, BiFunction<Int, String, Stu> { t, u -> Stu(u, t)}).thenAccept {
println(“name ${it.name}, age ${it.age}”)
}
- 异常转换、多次消费
val ff = CompletableFuture()
ff.handle{
_, _ -> 10
}.whenComplete{
t, u -> println(“first handler $t”)
}.whenComplete { t, u -> println(“second handler $t”)}
ff.obtrudeValue(null)
2.3 流结果设置
这里也通过表格方式,有下面几种方法
我们通过构造器生成时,需要自己设置值,如下
val ff = CompletableFuture()
ff.thenApply {
it / 2 + 4
}
ff.complete(16)
设置值后,后面的流才会执行
3. 源码解读
CompletableFuture是流的一个节点,内部持有了完成状态以及依赖其的任务节点信息,其内部同样实现了完成态时依赖任务执行处理;
3.1 数据结构
这主要体现这两个成员变量上
volatile Object result;
volatile Completion stack;
- result:结果为null,表示未执行;执行结果为空,则设置为静态常量NIL,异常则设置为AltResult实例,正常完成,则表示实际的值; AltResult内容如下
static final AltResult NIL = new AltResult(null);
static final class AltResult {
final Throwable ex;
AltResult(Throwable x) { this.ex = x; }
}
- stack:链表尾部指针,组成了后进先出的链表结构;是依赖当前完成状态需要执行的任务集合;内容如下,其实现ForkJoinTask,只是为了利用ForkJoinPoo线程池,其最大有点就是解决频繁的异步任务的,很配
abstract static class Completion extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
volatile Completion next;
abstract CompletableFuture<?> tryFire(int mode);
abstract boolean isLive();
public final void run() { tryFire(ASYNC); }
public final boolean exec() { tryFire(ASYNC); return false; }
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
}
对于stack处理
- postFire方法: 通知其依赖的节点,进行完成传播;由于没有使用锁,只使用了原子操作,这样可以防止,有些节点加入到依赖集合中,却不能得到执行
- cleanStack方法:清除失活以及无效的节点
- postComplete方法:执行stack集合中任务
- casStack方法:改变队尾操作
- tryPushStack方法:尝试加入队尾数据
- pushStack:队尾加入数据
3.2 Completion以及子类
Completion类,抽象类,待执行的任务节点;其内部持有下个流以及流任务执行的逻辑;其继承关系类图如下:
内部变量
CompletableFuture dep;
CompletableFuture src;
CompletableFuture snd
dep代表当前操作新成的流节点,src、snd为其依赖的流节点;其中每个类,还有流任务执行的对象:Runable、Function、ConSumer、BiFunction、BiConsumer等
tryFire方法很重要,其持有的转换对象、消费对象代表了需要执行的操作;其实他们对应的tryFire方法内部实际操作,都在CompletableFuture内有对应方法
tryFire方法
很关键的方法,其持有的转换对象、消费对象代表了需要执行的操作;其情况与具体的模式有关,其情况如下
- SYNC = 0, 同步状态;执行线程为当前方法调用线程或者上个流执行所在线程;同时其可能仅仅是为了启动线程池启动任务
- ASYNC = 1,异步,表示需要在线程池内
执行 - NESTED = -1,传播模式,表示依赖的流节点已经处于完成状态,正在传递处理
claim方法
线程池任务提交,并且执行有且提交一次
3.3 中间流生成与执行原理
中间流处理,就是CompletionStage声明的方法;其系列处理方法,基本逻辑相同,也就是方法名称不同而已,而由于持有的任务不同而略有不同
3.3.1 thenRun系列
均是通过私有方法uniRunStage进行处理,进行添加时尝试处理的
private CompletableFuture uniRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
if (e != null || !d.uniRun(this, f, null)) {
UniRun c = new UniRun(e, d, this, f);
push©;
c.tryFire(SYNC);
}
return d;
}
对于此方法有下面逻辑
- 同步执行,且uniRun执行成功,则返回生成流节点
- 否则,添加相应Completion子类到等待集合中,并再次尝试执行;和之前提到的postFire结合确保一定能够执行
final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) {
Object r; Throwable x;
if (a == null || (r = a.result) == null || f == null)
return false;
if (result == null) {
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
completeThrowable(x, r);
else
try {
if (c != null && !c.claim())
return false;
f.run();
completeNull();
} catch (Throwable ex) {
completeThrowable(ex);
}
}
return true;
}
方法的最后一个参数,当是触发线程池提交任务操作时,需要传入任务实例,否则传入空指;也就是传入空指,代表此方法中直接执行,这时,线程可能为生成流节点方法线程,也可能是上个流节点执行的线程,也可能是线程池创建的线程中(好像等于白说了);这个方法流程如下:
-
检验依赖节点执行状态,未完成则结束
-
执行异常结束,则设置异常状态,结束
-
正常执行结束时,尝试执行当前任务
- 需要向线程池提交任务,则通过claim方法,进行处理,并返回;提交任务后会执行tryFire方法
- 不需要向线程池提交任务,执行;若执行成功,有结果直接设置结果,无结果设置NIL值;若是发生已成设置异常
如果调用CompletionStage声明的方法未能立刻执行的,则需要通过依赖的流节点完成后通过postComplete方法进行分发;
final void postComplete() {
CompletableFuture<?> f = this; Completion h; while ((h = f.stack) != null || (f != this && (h = (f = this).stack) != null)) { CompletableFuture<?> d; Completion t;
if (f.casStack(h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
h.next = null;
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
tryFire方法,返回空表示流节点任务没有完成,否则表示已完成,继续这个节点的分发;也就是分发时通过tryFire方法去执行依赖节点的任务
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助**。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-nWFDvwPm-1719051013471)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取