如何组合3个或更多CompletionStages?

时间:2015-11-30 17:49:09

标签: java concurrency java-8

如果有2个CompletionStages,我可以将它们与thenCombine方法结合使用:

CompletionStage<A> aCompletionStage = getA();
CompletionStage<B> bCompletionStage = getB();
CompletionStage<Combined> combinedCompletionStage =
    aCompletionStage.thenCombine(bCompletionStage, (aData, bData) -> combine(aData, bData));

如果我有3个或更多CompletionStages,我可以创建一个thenCombine方法链,但我必须使用临时对象来传递结果。例如,以下是使用Pair包中的Tripleorg.apache.commons.lang3.tuple的解决方案:

CompletionStage<A> aCompletionStage = getA();
CompletionStage<B> bCompletionStage = getB();
CompletionStage<C> cCompletionStage = getC();
CompletionStage<D> dCompletionStage = getD();

CompletionStage<Combined> combinedDataCompletionStage =
        aCompletionStage.thenCombine(bCompletionStage, (Pair::of))
                .thenCombine(cCompletionStage, (ab, c) ->
                        Triple.of(ab.getLeft(), ab.getRight(), c))
                .thenCombine(dCompletionStage, (abc, d) ->
                        combine(abc.getLeft(), abc.getMiddle(), abc.getRight(), d));

是否有更好的方法来合并多个CompletionStages的结果?

7 个答案:

答案 0 :(得分:32)

将多个阶段结合起来的唯一方法就是使用CompletableFuture,这些阶段可以与越来越多的阶段进行良好的扩展。如果CompletionStage不是CompletableFuture,您仍然可以使用.toCompletableFuture()转换它们:

CompletableFuture<A> aCompletionStage = getA().toCompletableFuture();
CompletableFuture<B> bCompletionStage = getB().toCompletableFuture();
CompletableFuture<C> cCompletionStage = getC().toCompletableFuture();
CompletableFuture<D> dCompletionStage = getD().toCompletableFuture();

CompletionStage<Combined> combinedDataCompletionStage = CompletableFuture.allOf(
    aCompletionStage, bCompletionStage, cCompletionStage, dCompletionStage)
    .thenApply(ignoredVoid -> combine(
        aCompletionStage.join(), bCompletionStage.join(),
        cCompletionStage.join(), dCompletionStage.join()) );

这包含了比通过thenCombine组合两个阶段更多的样板文件,但是当向其添加更多阶段时,样板文件不会增长。

请注意,即使使用原始thenCombine方法,您也不需要TriplePair就足够了:

CompletionStage<Combined> combinedDataCompletionStage =
    aCompletionStage.thenCombine(bCompletionStage, (Pair::of)).thenCombine(
        cCompletionStage.thenCombine(dCompletionStage, Pair::of),
        (ab, cd) -> combine(ab.getLeft(), ab.getRight(), cd.getLeft(), cd.getRight()));

如果你想要结合更多阶段,它仍然不能很好地扩展。

中间解决方案(关于复杂性)可能是:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(b -> cCompletionStage.thenCompose(
        c -> dCompletionStage.thenApply(d -> combine(a, b, c, d)))));

这在结构上更简单,但是在更多阶段仍然无法很好地扩展。

答案 1 :(得分:2)

Holger's third answer可以缩短一点:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(
        b -> cCompletionStage.thenCombine(dCompletionStage,
            (c, d) -> combine(a, b, c, d))));

答案 2 :(得分:2)

我认为CompleableFuture.allOf()函数可以为您提供帮助。

例如:(查看完整的课程here

    List<String> urls = [
            "https://webhook.site/1647465b-c28f-4ffe-bbfe-5d3ad95ef994",
            "https://webhook.site/1647465b-c28f-4ffe-bbfe-5d3ad95ef994?a=1"
    ]
    CompletableFuture<Response>[] futures = new Completablefuture[2]
    for (int i = 0; i < urls.size(); i++) {
        futures[i] = asyncHttpClient.prepareGet(urls[i]).execute().toCompletableFuture()

    }

    CompletableFuture.allOf(futures).thenApply { future ->
        return futures.collect { it.join() }
    }.thenApply({ responses ->
        //Do something with results
        responses.each { println("Status code: " + it.statusCode) }
    })

答案 3 :(得分:2)

CompletableFuture的任何数量都可以组合(减少)

CompletionStage<A> futA = getA();
CompletionStage<B> futB = getB(); 
CompletionStage<C> futC = getC(); 

Stream.of(futA, futB, futC) 
    .reduce((f1, f2) -> f1.thenCombine(f2, (d1, d2) -> combine(d1, d2));

combine 方法的实现将负责合并数据值(A,B和C),如果A,B和C完全不同,则可能会很棘手。

答案 4 :(得分:1)

我认为您应该使用一个中间对象,但不要使用PairTuple

public R method() {
    CompletableFuture<A> aFuture = getAFuture();
    CompletableFuture<B> bFuture = getBFuture();
    CompletableFuture<C> cFuture = getCFuture();
    CompletableFuture<D> dFuture = getDFuture();

    return CompletableFuture.completedFuture(new WellNamedResultHolder())
            .thenCombineAsync(aFuture, WellNamedResultHolder::withAResult)
            .thenCombineAsync(bFuture, WellNamedResultHolder::withBResult)
            .thenCombineAsync(cFuture, WellNamedResultHolder::withCResult)
            .thenCombineAsync(dFuture, WellNamedResultHolder::withDResult)
            .thenApplyAsync(this::combineAllTheResults);
}

private static class WellNamedResultHolder {
    private A aResult;
    private B bResult;
    private C cResult;
    private D dResult;

    // Getters

    public WellNamedResultHolder withAResult(final A aResult) {
        this.aResult = aResult;
        return this;
    }

    public WellNamedResultHolder withBResult(final B bResult) {
        this.bResult = bResult;
        return this;
    }

    public WellNamedResultHolder withCResult(final C cResult) {
        this.cResult = cResult;
        return this;
    }

    public WellNamedResultHolder withDResult(final D dResult) {
        this.dResult = dResult;
        return this;
    }
}

结果持有人的实际形式显然可以更改以满足您自己的需求,从而为您提供更大的灵活性。您还需要负责这些期货的完成情况。尽管样板更多,但是您获得的代码可以更描述所发生的事情(龙目岛可以整理出哪些内容)。

答案 5 :(得分:0)

我遇到了类似的问题,但是有3个以上的completablefutures,因此基于Holger's的答案,我开发了一个小型通用实用程序。

public static <T, R> CompletableFuture<R> allOf(List<CompletableFuture<T>> args, Function<List<T>, R> combiner) {
    final Queue<CompletableFuture<T>> queue = new LinkedList<>();
    for (CompletableFuture<T> arg : args) {
        queue.add(arg);
    }
    return aggregator(queue, new ArrayList<>(), combiner);
}

private static <T, R> CompletableFuture<R> aggregator(Queue<CompletableFuture<T>> queue, List<T> arg,
        Function<List<T>, R> combiner) {
    if (queue.size() == 2)
        return queue.poll().thenCombine(queue.poll(), (c, d) -> {
            arg.add(c);
            arg.add(d);
            return combiner.apply(arg);
        });
    return queue.poll().thenCompose(data -> {
        arg.add(data);
        return aggregator(queue, arg, combiner);
    });
}

答案 6 :(得分:0)

您可以创建一个辅助函数...

combine3(
    futureA, futureB, futureC,
    (a, b, c) -> {
      // let's go!
    }).toCompletableFuture();

定义:

private static <A, B, C, D> CompletionStage<D> combine3(
        CompletionStage<A> aStage,
        CompletionStage<B> bStage,
        CompletionStage<C> cStage,
        TriFunction<A, B, C, D> f
) {
    return aStage.thenCompose(
            a -> bStage.thenCombine(cStage,
                    (b, c) -> f.apply(a, b, c)));
}

interface TriFunction<A, B, C, D> {
    D apply(A a, B b, C c);
}