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

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


在上篇文章 《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方法:
uniHandleStage
这个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,并将其方法压栈
  • 当第一个结果执行完成后,将方法出栈,执行第二个方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值