链接的文章,只是在我找源码的时候,发现,这篇文章写的很好,分享给大家,也激励自己往这方面努力吧。 Completable别人的教程链接
此篇文章是在分享文章的基础上的扩充,主要分为两个部分,第一部分是算法部分,再次阅读下Completable的核心算法,第二部分是扩充功能部分,比如Api 中参数Executor的作用、Async的作用、多次Completable的怎么处理。
算法部分
从链接文章中的结构流程示意图中,是不是很明显可以感受到,这里是一个树结构。这里不像二叉树,有左子树、右子树,这里的子节点是没有定量的,由next,将一个节点的兄弟节点链接起来,用src,stack 来连接好父子节点 。
从源码也可见一般啊,见源码中的注释,用一个push,就可以源源不断的构建子节点。
private <V> CompletableFuture<V> uniApplyStage(
Executor e, Function<? super T,? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<V> d = new CompletableFuture<V>();
if (e != null || !d.uniApply(this, f, null)) {
// 这里就是在构建一个子节点,包含了子Completable d,父Completable this,以及方法f
UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
//这里的push,就是要将添加一个子节点
push(c);
c.tryFire(SYNC);
}
return d;
}
// 不断尝试添加
final void push(UniCompletion<?,?> c) {
if (c != null) {
while (result == null && !tryPushStack(c))
lazySetNext(c, null); // clear on failure
}
}
//添加的方法
final boolean tryPushStack(Completion c) {
//h为当前链表的头节点,也可以说压栈的栈顶。
Completion h = stack;
//将当前节点c的next指向h,那么c就是链表的头节点
lazySetNext(c, h);
//再将stack的栈顶指向c。
return UNSAFE.compareAndSwapObject(this, STACK, h, c);
}
既然是树的结构,那么触发(从父节点开始触发),就涉及到树的遍历。单从所有的节点都会从一个线程中运行说起。
这段是核心算法,也是遍历的过程
- 1.首先抛个引子,二叉树的先序遍历,以迭代的方式,
- 2.迭代过程是,一个循环不断的从栈顶取节点,取出后又将 此节点的右子节点 左子节点依次压入栈中.
- 3.再来思考,CompletableFuture的结构,CompletableFuture的stack 指向的是Completion,而Completion 又包含了CompletableFuture.
- 可以理解为根节点保存了一个栈顶元素C,C中的内容包含了一个子CompletableFuture,同时栈中的Next 也指向根节点的下一个儿子C,即这些C的src都指向根节点,相信上面的图,也是这么表示的.
- 4.这次遍历算法中的f,可以是根节点也有可能不是根节点,当不是根节点时,就相当于引子中的栈顶节点(已经完成了子节点f的计算),需要f的栈顶元素压入根节点的栈中.其中需要判断 f 是否还有很多栈元素(即C >1),如果C>1,就需要将f的栈元素压入根节点的栈,保留最后一个,最后一个也会成为栈顶元素,就不必再压入栈,直接计算即可.
- 5.因为Completion中以链表存储很多Completion.所以在Completion压入根节点栈时,是以链表的遍历方式,从头节点开始依次压入根节点栈中.
此算法,设计精妙没有赘余,出自高手啊.
final void postComplete() {
/*
* On each step, variable f holds current dependents to pop
* and run. It is extended along only one path at a time,
* pushing others to avoid unbounded recursion.
*/
CompletableFuture<?> f = this; Completion h;
//如果f的栈顶元素不为null,或者f不是根节点,就要将f的栈元素压入根节点的栈中了.
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture<?> d; Completion t;
//f的stack 指向stack的next
if (f.casStack(h, t = h.next)) {
//next不为null,说明f的Completion>1,考虑f不是根节点时,要压栈
if (t != null) {
//不是根节点
if (f != this) {
//压栈,这里的this 是根节点,压入根节点的栈中
pushStack(h);
continue;
}
h.next = null; // detach
}
//这里就是操作h,燃烧h,获得计算结果,如果h中的Completable中没有栈顶元素就返回null,有则返回h中的Completable。当然,这是没有多线程时的处理逻辑。
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
好了,核心算法部分就先到这里,接下来,就要介绍,Api参数中Execute的作用,以及Async的用处。
其实多线程的加入,主要体现在遍历的区别,当一个子节点需要在其它线程运行时,那么就从此子节点开始为新的根节点,在其它线程开始遍历。
而源码中的体现在于
//这是completion类的方法,
final boolean claim() {
Executor e = executor;
if (compareAndSetForkJoinTaskTag((short)0, (short)1)) {
if (e == null)
return true;
executor = null; // disable
//用其它的线程来运行此节点
e.execute(this);
}
return false;
}
// 在同样是tryFire的中,因为uiApply()里会调用claim(),所以,根节点在运行此方法时
// 会返回null,那么根节点就不再遍历此节点.
// 此节点会在别的线程运行此方法,并且继续遍历此节点的子节点(如果有)
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
if ((d = dep) == null ||
!d.uniApply(a = src, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
那么Async 呢?从api的角度,主要体现在线程池的选择不同。
public <U> CompletableFuture<U> thenApply(
Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(
Function<? super T,? extends U> fn) {
return uniApplyStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(
Function<? super T,? extends U> fn, Executor executor) {
return uniApplyStage(screenExecutor(executor), fn);
}
接下来再来聊一聊,applyToEither 和 thenCombine 这两个api,共同点是两者都两个Completable组成,来影响结果,区别是一个是or,一个是and。来看源码。
这里只贴核心逻辑,不贴跳转逻辑
//applyToEither的部分核心逻辑,a代表一个CompletableFuture,b代表一个CompletableFuture,
//一个有a或b,任意一个有结果了,就可以往下执行。
final <R,S extends R> boolean orApply(CompletableFuture<R> a,
CompletableFuture<S> b,
Function<? super R, ? extends T> f,
OrApply<R,S,T> c) {
Object r; Throwable x;
if (a == null || b == null ||
((r = a.result) == null && (r = b.result) == null) || f == null)
return false;
//thenCombine 的部分核心逻辑,a代表一个CompletableFuture,b代表一个CompletableFuture,
//如果a、b中任意一个没有结果都不往下执行。
final <R,S> boolean biApply(CompletableFuture<R> a,
CompletableFuture<S> b,
BiFunction<? super R,? super S,? extends T> f,
BiApply<R,S,T> c) {
Object r, s; Throwable x;
if (a == null || (r = a.result) == null ||
b == null || (s = b.result) == null || f == null)
return false;
好了,今天就要这了,当然,还有allOf 、anyOf 的api,留给感兴趣的朋友去自己探究。
ps:流程图来自流程图