首页 > 编程语言 >JDK源码之CompletableFuture(二)链式调用原理

JDK源码之CompletableFuture(二)链式调用原理

时间:2022-12-21 12:05:02浏览次数:45  
标签:返回 调用 JDK 源码 CompletableFuture 方法 stack


​​JDK源码之CompletableFuture(一)结果返回原理​​JDK源码之CompletableFuture(二)链式调用原理
JDK源码之CompletableFuture(三)anyOf,allOf是怎么实现的?

目录

  • ​​一、第一步调用的返回结果是什么?​​
  • ​​二、CompletableFuture的重要属性​​
  • ​​三、CompletableFuture的Stack属性​​
  • ​​四、为什么压栈?​​
  • ​​五、什么时候触发出栈呢?​​
  • ​​六、总结​​


在上篇文章

​​《JDK源码之CompletableFuture(一)结果返回原理

》​​,我们了解了CompletableFuture的创建。关于CompletableFuture是可以进行链式调用的,其使用方式如下:

CompletableFuture<Integer> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("supplyAsync");
});

voidCompletableFuture.handleAsync((num, e) -> {
System.out.println("handleAsync3333333" + num);
return "yes";
});

创建后,返回回来的CompletableFuture对象,还可以进行再次的异步调用,并且可以使用上次的返回结果。这个是如何实现的?

一、第一步调用的返回结果是什么?

如果要弄清楚这个问题,我们要先搞清楚第一步的返回结果voidCompletableFuture是什么。
在asyncSupplyStage方法中,会调用这么一段方法:

static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
Supplier<U> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<U> d = new CompletableFuture<U>();
e.execute(new AsyncSupply<U>(d, f));
return d;
}

可以看到返回的是新创建的一个CompletableFuture,命名为d,将其返回出去供外部使用。

任务的执行结果也是放在d这个CompletableFuture中

二、CompletableFuture的重要属性

CompletableFuture有两个最为重要的属性,如下:

volatile Object result;       // 返回结果
volatile Completion stack; // 依赖的CompletableFuture

第二个属性stack的含义,可能理解起来不是那么容易,我下面根据源码跟你详细解释下

三、CompletableFuture的Stack属性

当有第二个任务加入的时候,它会执行uniHandleStage方法,表示下一个阶段的方法:

这个方法中,最重要的是我标红的这个push方法:

JDK源码之CompletableFuture(二)链式调用原理_Completable


这个push方法是做了什么呢?我就以tryPushStack这个方法为例来为你介绍,

final boolean tryPushStack(Completion c) {
Completion h = stack;
lazySetNext(c, h);
return UNSAFE.compareAndSwapObject(this, STACK, h, c);
}

为了说明清楚,我把第一个执行的supplyAsync方法称为第一个方法
把第二个执行的handleAsync方法称为第二个方法

  • 入参c:入参c是一个包含了第二个方法的方法的UniCompletion
  • tryPushStack:这个方法是在第一个方法返回的CompletableFuture中进行的执行。
  • tryPushStack:这个方法将c置为stack属性,并将原来的stack参数置为了c的next(就是将入参c压栈)。

四、为什么压栈?

压栈是为了实现返回的CompletableFuture的复用,举例如下:

integerCompletableFuture.handleAsync((num, e) -> {
System.out.println("handleAsync3333333" + num);
return "yes";
});
integerCompletableFuture.handleAsync((num, e) -> {
System.out.println("handleAsync" + num);
return "yes";
});

这样integerCompletableFuture就可以复用,这个操作也是很牛的。

五、什么时候触发出栈呢?

所有的CompletableFuture在执行完后都会调用一个postComplete();
里面就触发的出栈操作,并调用tryFire方法

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;
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;
}
}

六、总结

这篇文章看完,你应该对CompletableFuture的链式调用原理比较清楚了,在此我再次梳理一下:

  • 第一步调用返回了第一个结果future
  • 第二步调用使用了第一个结果的future,并将其方法压栈
  • 当第一个结果执行完成后,将方法出栈,执行第二个方法


标签:返回,调用,JDK,源码,CompletableFuture,方法,stack
From: https://blog.51cto.com/u_11970680/5959694

相关文章