传递给 CompletableFuture.allOf() 的所有期货都会运行吗?

Posted

技术标签:

【中文标题】传递给 CompletableFuture.allOf() 的所有期货都会运行吗?【英文标题】:Will all futures passed to CompletableFuture.allOf() run? 【发布时间】:2019-06-17 05:36:40 【问题描述】:

所以我有几个希望运行的未来,即使有些失败我希望所有人都有机会运行。所以如果我这样做:

CompletableFuture.allOf(futures).join()

会这样吗?我的理由是,每个未来都会在其执行程序中拥有自己的队列作业,因此只要主线程没有首先完成,所有的都会运行。我的问题是我在.allOf() 上专门.join() 所以我的应用程序在运行所有内容之前不会结束

所以allOf() 语义让我感到困惑:无论是否成功,当所有传递的期货都完成时,未来是否会返回完成?或者如果它看到一个失败而不等待其余的,它会完成一个失败的未来吗?

编辑

为了进一步说明我的问题,.allOf 的行为是这样的吗:

Stream.of(futures).forEach(future -> 
  try 
    future.join()
   catch (Throwable e) 
    //dont throw, we want to join the rest
  
)

或者它的行为如下:

Stream.of(futures).forEach(future -> 
  try 
    future.join()
   catch (Throwable e) 
    throw e; //All other remaining .join() wont run
  
)

它是什么?第一种还是第二种情况?因为我想要第一个案例,这就是我暂时在我的代码上使用的,但我想尽可能使用allOf(),因为它更美观

谢谢!

【问题讨论】:

来自doc: "此方法的应用之一是在继续程序之前等待一组独立的CompletableFutures完成,如:CompletableFuture.allOf(c1, c2, c3).join();." - 至于突然完成,也有记录。 @VinceEmigh 我认为问题是不同的,如果其中一个失败,并且您知道此时结果将是CompletionException,那么所有其他可能尚未开始的仍然被执行?我的意思是假设他们支持中断,会发送中断吗? @VinceEmigh 我没有看到任何关于“突然完成”的信息,所以如果你能把它拼出来就好了。 @Eugene 是对的,还要补充他的隐喻,allOf() 会等到所有尚未执行的未来运行并完成(失败或其他)吗? 值得注意的是,像join() 这样的东西不会使期货运行。未来对象所代表的异步操作已经在运行(或至少计划运行)——否则你一开始就不可能有未来。未来的操作可能会失败或被取消,但这与您等待它们的方式无关。 公平点@DanielPryden,我的目的不是让它们运行,而是等到所有完成,不管是失败还是成功,因为否则我的主线程将结束,让工作未完成。如果allOf() 会立即失败并继续说RuntimeException,那就太糟糕了。所以问题是:allOf() 是否等到全部完成? 【参考方案1】:

是的,每个未来都将独立尝试完成。

我认为您也在尝试了解控制在各种情况下的流动方式。我想出了 4 个场景:

    由于未处理的异常而发生故障的未来 应明确标记为失败的 future 并带有一个 completeExceptionally 并且在其尾部有一个异常块。 应使用 completeExceptionally AND 明确标记为失败的未来,其尾部没有异常块。 将走向成功的未来。
//CASE 1
// A future that shall fail due to an unandled exception in its run 
// and has an exceptionally block at its tail
CompletableFuture<Void> unhandledFailureFutureWithExceptionHandler =
    CompletableFuture.runAsync(() -> 
        throw new RuntimeException("Exception in unhandledFailureFutureWithExceptionHandler");
    );
unhandledFailureFutureWithExceptionHandler = unhandledFailureFutureWithExceptionHandler
    .exceptionally(throwable -> 
        // Handling exception for this future
        // HANDLING POINT 1
        System.out.println("Handling exception at HANDLING POINT FOR CASE 1, 
            failure message is : " + throwable.getMessage());
        return null;
    );

//CASE 2
//A future that shall fail and has an exceptionally block at its tail
CompletableFuture<Void> failedFutureWithExceptionHandler = new CompletableFuture<>();
failedFutureWithExceptionHandler.completeExceptionally(
    new RuntimeException("Exception in failedFutureWithExceptionHandler")
);
failedFutureWithExceptionHandler = failedFutureWithExceptionHandler.exceptionally(throwable -> 
    // Handling exception for this future
    // HANDLING POINT 2
    System.out.println("Handling exception at HANDLING POINT FOR CASE 2, 
        failure message is : " + throwable.getMessage());
    return null;
);

//CASE 3
//A future that shall fail and has no exceptionally block at its tail
CompletableFuture<Void> failedFutureWithoutExceptionHandler = new CompletableFuture<>();
failedFutureWithoutExceptionHandler.completeExceptionally(
    new RuntimeException("Exception in failedFutureWithoutExceptionHandler")
);

//CASE 4
//A future that shall succeed and print a message to console
CompletableFuture<Void> successFuture = CompletableFuture.runAsync(() -> 
    System.out.println("CASE 4 : Running successFuture")
);

CompletableFuture.allOf(unhandledFailureFutureWithExceptionHandler, 
    failedFutureWithExceptionHandler, failedFutureWithoutExceptionHandler, successFuture)
        .exceptionally(throwable -> 
            // Handling exception if ANY of the futures that did not have its own exceptionally block
            // In this case the exception of `failedFutureWithoutExceptionHandler` will be handled here
            // HANDLING POINT 3
            System.out.println("Handling exception at HANDLING POINT FOR CASE 3, 
                failure message is : " + throwable.getMessage());
            return null;
        ).join();

控制台产生的输出是

Handling exception at HANDLING POINT FOR CASE 1, failure message is : java.lang.RuntimeException: Exception in unhandledFailureFutureWithExceptionHandler
Handling exception at HANDLING POINT FOR CASE 2, failure message is : Exception in failedFutureWithExceptionHandler
CASE 4 : Running successFuture
Handling exception at HANDLING POINT FOR CASE 3, failure message is : java.lang.RuntimeException: Exception in failedFutureWithoutExceptionHandler

正如你所看到的,如果一个 future 抛出一个未处理的错误,如案例 1,如果它有一个 exceptionally 块链接到它的尾部,则应在该点处理异常

对于情况2,如果future被completeExceptionally标记为失败,如果future有一个链接到它的尾部的处理程序,那么exceptionally块应该由那个块处理

在情况3中,future被标记为失败并且没有异常块,因此它应该由下一级的exceptionally块处理,在这种情况下它是@的exceptionally块987654328@.

如您所见,案例 4 运行完成,并且无论其他期货是否失败,消息都会在控制台上打印。

【讨论】:

以上是关于传递给 CompletableFuture.allOf() 的所有期货都会运行吗?的主要内容,如果未能解决你的问题,请参考以下文章

无法正确地将值从父母传递给孩子和孩子传递给父母

如何将对象数组作为道具传递给组件,然后将数组的成员作为道具传递给嵌套组件?

为啥将 ClientID 传递给 javascript 函数会传递整个控件?

将变量传递给 jQuery 插件参数

如何将环境变量传递给传递给xmllint的命令?

将 JSON 数据从 php 传递给 html-data 属性,然后传递给 Javascript