CompletableFuture 讲解系列
前言
CompletableFuture
估计多数程序员都了解那么一丢丢,
网上介绍 API 的文章一大把,至于原理么,呵呵,不讲。
能把源码讲清楚的文章,约等于 0,甚少我一直没找到。
Doug Lea 的代码,可谓鬼斧神工,精妙绝伦,难懂!
今儿在这儿,我壮胆写篇文章,剖析下源码,探究下原理。
各位路过的大佬儿,还请多多指教。若有谬误,烦请指正。
一、CompletableFuture 是什么?
CompletableFuture
可以简单理解为 Future
的升级版,可以非常方便的进行任务的编排。
以往任务编排,需要借助 CountDownLatch、Semaphore、CyclicBarrier
等来实现,处理逻辑较为复杂。
本文源码层面分析 allOf
和 anyOf
这两个方法。抽丝剥茧,深入剖析 CompletableFuture
的原理。
二、代码示例
1. allOf(CompletableFuture<?>... cfs)
allOf : 可以探测,所有的异步任务,是否全部执行完了。
举个例子,集齐七颗龙珠可以召唤神龙。
就可以分七个支线,每条支线找一颗龙珠。
// 不用CompletableFuture版本
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
long start = System.currentTimeMillis();
int count = 7;
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
int num = i + 1;
pool.submit(() -> {
new Dragon(num).findPreciousness();
latch.countDown();
});
}
String mainThread = Thread.currentThread().getName();
System.out.println(mainThread + "收集龙珠,等待中……");
latch.await();
long end = System.currentTimeMillis() - start;
System.out.println(mainThread + "线程历经" + end + "毫秒,终于集齐七颗龙珠,开始召唤神龙!!");
pool.shutdown();
}
static class Dragon {
private int num;
Dragon(int num) {
this.num = num;
}
private void findPreciousness() {
int cost = RandomUtil.randomInt(1, num + 1) * 1000;
try {
Thread.sleep(cost);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程历经 " + cost + " 毫秒找到第 " + num + " 颗龙珠");
}
}
这个异步版本,用 CountDownLatch
来保证,所有异步任务都执行完。
如果不知道它怎么用,可以看我之前的文章《CountDownLatch源码分析》
集齐七颗龙珠的任务,交给线程池异步处理,
当子任务全部完成时,主线程继续执行,否则阻塞。
再看下,用 CompletableFuture
的 allOf
方法实现的版本
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
long start = System.currentTimeMillis();
int count = 7;
CompletableFuture[] temp = new CompletableFuture[count];
for (int i = 0; i < count; i++) {
int num = i + 1;
temp[num - 1] = CompletableFuture.runAsync(() -> new Dragon(num).findPreciousness(), pool);
}
String mainThread = Thread.currentThread().getName();
System.out.println(mainThread + "收集龙珠,等待中……");
CompletableFuture.allOf(temp).join();
long end = System.currentTimeMillis() - start;
System.out.println(mainThread + "线程历经" + end + "毫秒,终于集齐七颗龙珠,开始召唤神龙!!");
pool.shutdown();
}
示例中 allOf
方法,实现了 CountDownLatch
的功能。
当然,如果你对 stream
写法很熟悉,那代码可以很简单。
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
CompletableFuture[] futures = IntStream.rangeClosed(1, 7)
.mapToObj(num -> new Dragon(num))
.map(dragon -> CompletableFuture.runAsync(dragon::findPreciousness, pool))
.toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(futures).join();
pool.shutdown();
}
2. anyOf(CompletableFuture<?>... cfs)
anyOf : 所有的异步任务中,任意一个完成了,就可以被探测到。
举个例子,集齐七颗龙珠可以召唤神龙,这个任务比较难。
找到任意一颗,就摆两桌庆祝下,不管是哪个支线找到的。
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
CompletableFuture[] futures = IntStream.rangeClosed(1, 7)
.mapToObj(num -> new Dragon(num))
.map(dragon -> CompletableFuture.runAsync(dragon::findPreciousness, pool))
.toArray(size -> new CompletableFuture[size]);
CompletableFuture.anyOf(futures).join();
pool.shutdown();
只需要修改一行代码,就可以了。把 allOf
改为 anyOf
即可。
同样用 CountDownLatch
也可以实现,也是改一行
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch 是怎么实现的,不是本文的重点,不展开说了。
有兴趣的话,可以看我之前的文章《CountDownLatch源码分析》。
三、allOf 原理分析
1. 整体流程解释
截图中的1,指主线程提交任务到线程池。
截图中的2,指主线程调用 allOf
方法,生成 一个 CompletableFuture
对象 allOfFuture
。
线程池的工作线程,会执行异步任务,所有异步任务执行完,会给刚刚那个 allOfFuture
对象打个标识。
截图中的3,是主线程会判断那个标识,标识存在,说明所有异步任务已执行完。
若标识不存在,主线程阻塞,等待所有异步任务执行结束时,主线程会被唤醒。
以上是基本的原理,很重要。网上的文章很少会介绍这个。
我是前前后后,看了好多遍才想明白的,我也想找现成的文章看,可是压根就没有。
先有个印象,接着一条一条详细说。
2. runAsync 任务提交
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
CompletableFuture<Void> d = new CompletableFuture<Void>();
e.execute(new AsyncRun(d, f));
return d;
}
static final class AsyncRun extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
CompletableFuture<Void> dep; Runnable fn;
AsyncRun(CompletableFuture<Void> dep, Runnable fn) {
this.dep = dep; this.fn = fn;
}
}
相关代码,简洁明了,生成异步任务,提交到线程池,最终返回的是一个CompletableFuture
对象
简单画了张图,runAsync
返回的对象是截图中黄色的那个,给它的编号是 11
提交到线程池的对象,是截图中红色那个,其中 dep 属性指向了 11 的对象。
线程池的工作原理 ,本文不讲。任务执行时,会调用任务对象的 run()
方法。
如果对线程池的工作原理不清楚,可以看这篇《线程池工作原理》。
顺便说一句,CompletableFuture
两个属性需要关注,
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
result
是任务执行的结果, stack
等会再说,先记住它是链式结构
在七龙珠的任务中,会产生7个任务,同时也会返回7个 CompletableFuture
对象。
3. allOf 对象编排
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs,
int lo, int hi) {
CompletableFuture<Void> d = new CompletableFuture<Void>();
if (lo > hi) // empty
d.result = NIL;
else {
CompletableFuture<?> a, b;
int mid = (lo + hi) >>> 1;
if ((a = (lo == mid ? cfs[lo] :
andTree(cfs, lo, mid))) == null ||
(b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
andTree(cfs, mid+1, hi))) == null)
throw new NullPointerException();
if (!d.biRelay(a, b)) {
BiRelay<?,?> c = new BiRelay<>(d, a, b);
a.bipush(b, c);
c.tryFire(SYNC);
}
}
return d;
}
allOf 方法,是递归方法,有点不好理解,以七龙珠为例,画图来讲解。
首次调用 andTree(cfs, 0, 6) 时,a 就是 andTree(cfs, 0, 3) ,b 就是 andTree(cfs, 4, 6) 。
第二次调用 andTree(cfs, 0, 3) 时,a 就是 andTree(cfs, 0, 1) ,b 就是 andTree(cfs, 2, 3) ……
画了个大概的图,异步任务1的dep指向 cfs[0],,先记住这个,下文会用。
详细说下 andTree(cfs, 0, 1) 这个方法的调用 ,代码可简化为
CompletableFuture<Void> d = new CompletableFuture<Void>();
CompletableFuture<?> a = cfs[0];
CompletableFuture<?> b = cfs[1];
if (!d.biRelay(a, b)) {
CompletableFuture.BiRelay<?,?> c = new CompletableFuture.BiRelay<>(d, a, b);
a.bipush(b, c);
c.tryFire(SYNC);
}
return d;
3.1 d.biRelay(a, b),判断任务是否执行
首先看 d.biRelay(a, b)
这个方法。假设 任务1,任务2都还没执行,
CompletableFuture 的 result 字段,是volatile 修饰的,可保证线程间可见性。
可简单理解为:result 是 null 任务没执行,不是 null 任务已执行。
boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
Object r, s; Throwable x;
if (a == null || (r = a.result) == null ||
b == null || (s = b.result) == null)
return false;
if (result == null) { // 若进入这个分支,一定会将 result 赋值
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
completeThrowable(x, r);
else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
completeThrowable(x, s);
else
completeNull();
}
return true;
}
假设任务1 和任务2都没有执行 ,if (!d.biRelay(a, b)) {}
条件成立,进入这个分支。
3.2 a.bipush(b, c),对象编排
BiRelay<?,?> c = new BiRelay<>(d, a, b);
a.bipush(b, c);
前面说过 CompletableFuture
还有一个属性 stack
,类型是 Completion
,
这里的 new BiRelay<>(d, a, b)
,其中 BiRelay
就是 Completion
的一个子类。
@SuppressWarnings("serial")
static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And
BiRelay(CompletableFuture<Void> dep,
CompletableFuture<T> src,
CompletableFuture<U> snd) {
super(null, dep, src, snd);
}
}
BiRelay 对象中有 3 个属性,dep, src, snd,画张图,并标明指向关系
BiRelay<?,?> c = new BiRelay<>(d, a, b);
这句代码中:
c 指图中A对象,a 指 11 对象,b 指 12 对象, d 指图中 21 对象
final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
Object r;
while ((r = result) == null && !tryPushStack(c))
lazySetNext(c, null); // clear on failure
if (b != null && b != this && b.result == null) {
Completion q = (r != null) ? c : new CoCompletion(c);
while (b.result == null && !b.tryPushStack(q))
lazySetNext(q, null); // clear on failure
}
}
}
这代码看着就头大,主要是对象太多,容易迷,我画了图,多看几遍就好。
上面是 a.bipush(b, c)
的代码,还是假设 任务 1,任务2 没有执行,a、b 各自的 result 就是 null
tryPushStack( c ); 意思就是 把 a 的 stack 设置为 c,
b.tryPushStack(q); 意思就是把 b 的 stack 设置为 q,
q 是 new CoCompletion©,即 q 的 base 设置为 c;
对象比较多,看图吧
3.3 c.tryFire(SYNC); 若任务执行完,设置 result
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
if ((d = dep) == null || !d.biRelay(a = src, b = snd))
return null;
src = null; snd = null; dep = null;
return d.postFire(a, b, mode);
}
这个方法,下文还会讲到,这里 !d.biRelay(a = src, b = snd)
会返回 true, tryFire
返回 null.
至此 andTree(cfs, 0, 1)
结束,结合上图说,该方法执行时,会创建两个对象 A 和 B。
11 对象的 stack 指向A 。 12 对象 的 stack 指向 B
把这个过程理顺了,咱就想想 andTree(cfs, 0, 6)
,这个是递归,多想几遍就清楚了
如果画成图,大概是下面这个鸟样!!!!
最终这个 andTree(cfs, 0, 6)
,返回值就是 41 这个对象。
异步线程在执行完所有任务时,会给 41对象 result
赋值
主线程通过调用 join()
方法,判断 result 是否有值,来决定是否阻塞
4. join() 方法的阻塞与唤醒。
这里先假设,调用 join()
方法时,异步任务还没有执行完
public T join() {
Object r;
return reportJoin((r = result) == null ? waitingGet(false) : r);
}
reportJoin
这个方法可忽略,简单理解,把入参原样返回。(任务执行异常先不说)
result
有值,就直接返回,result
无值,调用 waitingGet(false)
4.1 waitingGet(false),阻塞或结束
private Object waitingGet(boolean interruptible) {
Signaller q = null;
boolean queued = false;
int spins = -1;
Object r;
while ((r = result) == null) {
if (spins < 0)
spins = SPINS;
else if (spins > 0) {
if (ThreadLocalRandom.nextSecondarySeed() >= 0)
--spins;
}
else if (q == null)
q = new Signaller(interruptible, 0L, 0L);
else if (!queued)
queued = tryPushStack(q);
else if (interruptible && q.interruptControl < 0) {
q.thread = null;
cleanStack();
return null;
}
else if (q.thread != null && result == null) {
try {
ForkJoinPool.managedBlock(q);
} catch (InterruptedException ie) {
q.interruptControl = -1;
}
}
}
if (q != null) {
q.thread = null;
if (q.interruptControl < 0) {
if (interruptible)
r = null; // report interruption
else
Thread.currentThread().interrupt();
}
}
postComplete();
return r;
}
while ((r = result) == null) {}
这句,跳出循环的条件就是 result
有值。
spins 这个就是自旋一会儿,可忽略,把 while 里的代码精简如下
while ((r = result) == null) {
// 自旋数次之后,执行下面的代码
Signaller q = new Signaller(interruptible, 0L, 0L);
tryPushStack(q) // 把 41 stack 设置为 q
ForkJoinPool.managedBlock(q); // 阻塞
}
public static void managedBlock(ManagedBlocker blocker)
throws InterruptedException {
ForkJoinPool p;
ForkJoinWorkerThread wt;
Thread t = Thread.currentThread();
if ((t instanceof ForkJoinWorkerThread) &&
(p = (wt = (ForkJoinWorkerThread)t).pool) != null) {
WorkQueue w = wt.workQueue;
while (!blocker.isReleasable()) {
if (p.tryCompensate(w)) {
try {
do {} while (!blocker.isReleasable() &&
!blocker.block());
} finally {
U.getAndAddLong(p, CTL, AC_UNIT);
}
break;
}
}
}
else {
do {} while (!blocker.isReleasable() &&
!blocker.block());
}
}
managedBlock
这个方法看起来,也很复杂,本文中是主线程调用的 join。
也就是普通线程,所有直接进 else
分支。
do {} while (!blocker.isReleasable() &&
!blocker.block());
public boolean isReleasable() {
if (thread == null)
return true;
if (Thread.interrupted()) {
int i = interruptControl;
interruptControl = -1;
if (i > 0)
return true;
}
if (deadline != 0L &&
(nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) {
thread = null;
return true;
}
return false;
}
public boolean block() {
if (isReleasable())
return true;
else if (deadline == 0L)
LockSupport.park(this);
else if (nanos > 0L)
LockSupport.parkNanos(this, nanos);
return isReleasable();
}
isReleasable
这个方法中,三个 if
条件都不满足,返回 false
;
block()
方法中,会进入第二个分支,会调用 park
方法,那进入阻塞,等待唤醒。
park 与 unpark 是怎么回事,可以看我之前的文章 《 park 和 unpark 》
至此,分析的 join() 方法的阻塞过程。唤醒过程等会儿再说。
4.2 tryFire 唤醒主线程
static final class Signaller extends Completion
implements ForkJoinPool.ManagedBlocker {
final CompletableFuture<?> tryFire(int ignore) {
Thread w; // no need to atomically claim
if ((w = thread) != null) {
thread = null;
LockSupport.unpark(w);
}
return null;
}
}
Signaller
类中 tryFire
方法会唤醒主线程,哪里调用的这个方法,等会儿再讲。
四、AsyncRun 原理分析
前面讲将任务提交到线程池时,e.execute(new AsyncRun(d, f));
AsyncRun 重写了 run 方法,线程池的工作线程会调用 run 方法。
static final class AsyncRun extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
CompletableFuture<Void> dep; Runnable fn;
AsyncRun(CompletableFuture<Void> dep, Runnable fn) {
this.dep = dep; this.fn = fn;
}
public void run() {
CompletableFuture<Void> d; Runnable f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
f.run();
d.completeNull();
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
}
1. d.completeNull() :result 赋值
前面的那张图,我重新贴出来
龙珠1 这个任务,dep 会指向 对象 11。 run 方法中 d.completeNull(); 就是设置 result
d.completeNull();
final boolean completeNull() {
return UNSAFE.compareAndSwapObject(this, RESULT, null,NIL);
}
2. d.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; // detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
这是核心方法,咱们慢慢说。下图做了简化,重点看与对象11相关的。
对象11的 postComplete
方法,f
就是对象11, h
就是对象A,第一次进入 while
循环。
if (f.casStack(h, t = h.next))
这句执行结果,就是 对象 11 stack 赋值为 null.
其中 t = h.next
,那么 t 就是 null.
if (f.casStack(h, t = h.next)) {
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
final boolean casStack(Completion cmp, Completion val) {
return UNSAFE.compareAndSwapObject(this, STACK, cmp, val);
}
h.tryFire(NESTED)
,即 A对象的 tryFire(-1)
,先说结果:
如果 对象 11 和 对象12,result 都有值,返回对象21,否则返回 null。
如果 对象 11 和 对象12,result 都有值,返回对象21,否则返回 null。
如果 对象 11 和 对象12,result 都有值,返回对象21,否则返回 null。
- h.tryFire(NESTED) 返回 null
如果 h.tryFire(NESTED)
返回null, 会造成 postComplete
方法结束。
因为 f = (d = h.tryFire(NESTED)) == null ? this : d;
f 赋值 this,即 f 还是对象 11
此时 (h = f.stack) != null
不成立,因为对象 11 stack 已经被赋值 null
f != this && (h = (f = this).stack) != null)
也不成立,因为 f 就是 this。
退出 while
循环,d.postComplete()
方法结束。
总结下,龙珠1 这个任务执行后,对象 11 的 result 被赋值 , stack 被清空。
此时 若龙珠 2 这个任务没完成,postComplete
方法结束。
- h.tryFire(NESTED) 返回 对象 21
那就继续 while
循环, 对象 21 stack
赋值为 null.
h.tryFire(NESTED)
,即 G 对象的 tryFire(-1)
,如此循环下去。
看上面那张图,咱们是由 对象11 来触发的,
如果 12 没完成就结束,否则启动下一轮,
看 对象 21 22 都完成没,没完成结束,完成启动下一轮,看31和32。
postComplete()
方法,确实不好懂,结合图,多看几遍。
3. h.tryFire(-1) :关联判断
前面说了 如果 对象 11 和 对象12,result 都有值,返回对象21,否则返回 null。
是为了 把 while 循环,退出和继续的条件讲清楚,现在分析**对象A **的 tryFire(-1)
3.1 h.tryFire(-1) 什么情况下返回 null
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
if ((d = dep) == null || !d.biRelay(a = src, b = snd))
return null;
src = null; snd = null; dep = null;
return d.postFire(a, b, mode);
}
boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
Object r, s; Throwable x;
if (a == null || (r = a.result) == null ||
b == null || (s = b.result) == null)
return false;
if (result == null) {
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
completeThrowable(x, r);
else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
completeThrowable(x, s);
else
completeNull();
}
return true;
}
现在对象11的result是有值的,如果对象12的result无值,
即 biRelay
方法中,第1个 if 判断中 (s = b.result) == null)
是 true,
也就是 tryFire
方法中 if ((d = dep) == null || !d.biRelay(a = src, b = snd))
是 true
至此可说明,对象12的 result
无值时,h.tryFire(-1)
返回 null.
3.2 h.tryFire(-1) 什么情况下返回 返回对象 21
对象12的result有值时,d.biRelay 这个方法,进入第二个 if 分支。
此分支里有3种情况,自己看吧,但不论哪种情况,对象 21 的 result
都会被赋值。
tryFire 方法执行 src = null; snd = null; dep = null;
,即 A对象三个属性被清空。
3.3 d.postFire(a, b, -1); d:对象21 , a:对象11 , b:对象12
final CompletableFuture<T> postFire(CompletableFuture<?> a,
CompletableFuture<?> b, int mode) {
if (b != null && b.stack != null) { // clean second source
if (mode < 0 || b.result == null)
b.cleanStack();
else
b.postComplete();
}
return postFire(a, mode);
}
final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
if (a != null && a.stack != null) {
if (mode < 0 || a.result == null)
a.cleanStack();
else
a.postComplete();
}
if (result != null && stack != null) {
if (mode < 0)
return this;
else
postComplete();
}
return null;
}
其中 b.cleanStack()
; 是 把对象12 stack 设置为 null,细节自己看源码吧
postFire(a, mode)
,第 1 个 if 分支不会进,因为 a.stack 是 null ,
会进第二个 if 分支 返回 this,即返回对象 21。
五、 主线程的唤醒。
1. 总体流程小结
AsyncRun 原理分析结束,结合龙珠任务总结如下。
- 七龙珠任务,被 allOf 两两结合,层层缩减为对象 41
- 线程池执行 龙珠1任务时,检测龙珠2任务,若其没完成,结束。
- 若龙珠2 完成了,会循环检测 21、22,以此类推。
- 结束最晚的一个任务,会层层判断,给对象 41的result 赋值
- 主线程调用对象41的 join方法,判断 result 是否有值,来决定是否阻塞。
2. 主线程何时被唤醒
之前讲waitingGet(false) 方法时,对象 41的 stack 会被赋值。
while ((r = result) == null) {
// 自旋数次之后,执行下面的代码
Signaller q = new Signaller(interruptible, 0L, 0L);
tryPushStack(q) // 把 41 stack 设置为 q
ForkJoinPool.managedBlock(q); // 阻塞
}
那对象 41的stack 是 Signaller
,postComplete
方法最后会调用 其 tryFire
,
这里有 LockSupport.unpark(w);
,即是唤醒主线程。
Signaller(boolean interruptible, long nanos, long deadline) {
this.thread = Thread.currentThread();
this.interruptControl = interruptible ? 1 : 0;
this.nanos = nanos;
this.deadline = deadline;
}
final CompletableFuture<?> tryFire(int ignore) {
Thread w; // no need to atomically claim
if ((w = thread) != null) {
thread = null;
LockSupport.unpark(w);
}
return null;
}
当然,如果主线程调用 join
方法时,其 result 已经有值,那就不会阻塞了。
六、anyOf 原理分析
它与 allOf 大同小异,简单说下
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}
static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs,
int lo, int hi) {
CompletableFuture<Object> d = new CompletableFuture<Object>();
if (lo <= hi) {
CompletableFuture<?> a, b;
int mid = (lo + hi) >>> 1;
if ((a = (lo == mid ? cfs[lo] :
orTree(cfs, lo, mid))) == null ||
(b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
orTree(cfs, mid+1, hi))) == null)
throw new NullPointerException();
if (!d.orRelay(a, b)) {
OrRelay<?,?> c = new OrRelay<>(d, a, b);
a.orpush(b, c);
c.tryFire(SYNC);
}
}
return d;
}
区别很小 OrRelay<?,?> c = new OrRelay<>(d, a, b);
这行不一样
static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or
OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src,
CompletableFuture<U> snd) {
super(null, dep, src, snd);
}
final CompletableFuture<Object> tryFire(int mode) {
CompletableFuture<Object> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
if ((d = dep) == null || !d.orRelay(a = src, b = snd))
return null;
src = null; snd = null; dep = null;
return d.postFire(a, b, mode);
}
}
final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
Object r;
if (a == null || b == null ||
((r = a.result) == null && (r = b.result) == null))
return false;
if (result == null)
completeRelay(r);
return true;
}
这个和 allOf 差别也很小,重点看这一个判断。
// allOf 里的版本
if (a == null || (r = a.result) == null ||
b == null || (s = b.result) == null)
return false;
// anyOf 里的版本
if (a == null || b == null ||
((r = a.result) == null && (r = b.result) == null))
return false;
allOf 里是集齐七龙珠,当龙珠1找到时,
若龙珠2没找到时,s = b.result) == null 是 true
,从而 tryFire 返回 null,终止。
anyOf 里找到任意一颗,当龙珠1找到时,(r = a.result) == null
这个条件是 flase,
(r = b.result) == null)
就不会判断,也就是龙珠2,找没找到可以忽略,tryFire 会继续。
进而一直 while 循环,直到 对象 41 的 result
赋值后才会结束。
大概就是这个意思,这个就是 anyOf 的原理,多看几遍就理解了。
总结
本文从源码层面,分析了 CompletableFuture 中 allOf / anyOf 两个方法
Doug Lea 真的是 YYDS,代码出了名的难懂。
但仔细理解,大神的代码,真的是精妙,尤其是多态运用,
让不同的行为,共用一套框架。
之所以我觉得难,不是代码的问题,是我功力不够。
再次膜拜中