为什么CompletableFuture allOf方法进行二进制搜索?

lim*_*756 8 java concurrency completable-future

我想知道allOf方法CompletableFuture是否进行轮询或进入等待状态,直到所有CompletableFutures传递给该方法的执行完成。我在其中查看了该allOf方法的代码,IntelliJ它正在执行某种二进制搜索。

请帮助我找出实际的allOf方法CompletableFuture

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    return andTree(cfs, 0, cfs.length - 1);
}

/** Recursively constructs a tree of completions. */
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;
}

/** Pushes completion to this and b unless both done. */
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
        }
    }
}

final CompletableFuture<V> tryFire(int mode) {
    CompletableFuture<V> d;
    CompletableFuture<T> a;
    CompletableFuture<U> b;

    if ((d = dep) == null ||
        !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
        return null;

    dep = null; src = null; snd = null; fn = null;

    return d.postFire(a, b, mode);
}
Run Code Online (Sandbox Code Playgroud)

Hen*_*olm 6

它不进行二进制搜索-它正在构建平衡的二叉树,输入期货位于叶子上,并且内部节点在两个子节点都完成时都完成。

出于某种原因(从代码中看不出来),代码的作者必须决定allOf(_,_)在两个期货之间进行原始操作是最有效的,如果他要求allOf(...)两个以上期货之间的交易,他就在制造它作为这些二进制基元的级联。

这棵树应该保持平衡,以便无论最后完成的未来是什么,在高层的未来可以完成之前,只有少数几个层次要崩溃。这可以提高某些情况下的性能,因为它可以确保我们完全完成工作之前,在CPU可能只是闲置,等待异步操作的情况下,可以完成尽可能多的工作。完成。

通过使最顶部的内部节点在其左子节点下与在其右子节点下的叶子数量一样多,来平衡树-这样,两个子节点都将获得原始数组的大约一半,然后代码从树的每一半中递归地构建一棵树。数组。分成两半看起来有点像二进制搜索的索引计算。


基本结构被似乎设计用于

  • 当某些原始期货已经完成时,使用分配较少的优化代码路径,并且
  • 确保allOf(_)带有一个元素的结果将返回一个新鲜的 CompleteableFuture。在大多数情况下,返回单个元素是可行的,但是作者必须要确保该库的用户可以使用新鲜的对象(如果他们将它们用作哈希映射或其他依赖于其他逻辑的键)能够分辨输入的输出,以及
  • throw new NullPointerException();使用?:和内联分配而不是诚实的if陈述,只有一个。这可能会产生较小的字节码,但会降低可读性。除非您亲自支付所得字节码的存储费用,否则不建议将其推荐为一种学习的方式。

  • 选择两个期货的完成方法的原因可能仅仅是因为它已经存在,例如`thenAcceptBoth`,`thenCombine`的后端。强制执行新期货的回报的原因是任何人都可以完成未来,例如,在其上调用“取消”,但在其值上也“完成”,如果这对输入的Future造成影响,则将是语义错误。使树平衡的原因是,它允许并行完成分支,而且在完成从属期货时还可以最大程度地减少堆栈溢出的风险。 (2认同)