当前位置: 首页 > 知识库问答 >
问题:

JavaCompletableFuture.allOf(). when完成()有多个例外

何睿范
2023-03-14

在Java官方文档中,它说

公共静态可完成未来

当所有给定的CompletableFutures完成时,返回一个新的CompletableFuture。如果任何给定的CompletableFutures异常完成,则返回的CompletableFuture也会异常完成,CompletionException将此异常作为其原因。

该文档没有指定多个给定的ComppletableFutures异常完成的情况。例如,在下面的代码片段中,如果 c1、c2、c3 都异常完成,异常及其原因是什么?

CompletableFuture.allOf(c1, c2, c3)
        .whenComplete((result, exception) -> {
            if (exception != null) {
                System.out.println("exception occurs");
                System.err.println(exception);
            } else {
                System.out.println("no exception, got result: " + result);
            }
        })

创建一个完整的Futuresignal_1signal_2,它们都完成得非常快。输出显示signal_1被传递给. when完成()作为异常的原因。

package com.company;

import java.util.concurrent.*;

public class Main {

    private static void runTasks(int i) {
        System.out.printf("-- input: %s --%n", i);

        CompletableFuture<Void> signal_1 = new CompletableFuture<>();
        signal_1.completeExceptionally(new RuntimeException("Oh noes!"));

        CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);

        CompletableFuture.allOf(signal_1, signal_2)
                .thenApplyAsync(justVoid -> {
                     final int num = signal_2.join();
                     System.out.println(num);
                     return num;
                })
                .whenComplete((result, exception) -> {
                    if (exception != null) {
                        System.out.println("exception occurs");
                        System.err.println(exception);
                    } else {
                        System.out.println("no exception, got result: " + result);
                    }
                })
                .thenApplyAsync(input -> input * 3)
                .thenAccept(System.out::println);

    }

    public static void main(String[] args) {
        runTasks(0);
    }

}
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!

Process finished with exit code 0

signal_1异常完成之前添加了3秒的睡眠,因此signal_1应该在signal_2之后完成。但是,输出仍然显示signal_1作为异常原因传递给. when完成()

package com.company;

import java.util.concurrent.*;

public class Main {

    static ExecutorService customExecutorService = Executors.newSingleThreadExecutor();

    private static void runTasks(int i) {
        System.out.printf("-- input: %s --%n", i);

        CompletableFuture<Void> signal_1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            throw new RuntimeException("Oh noes!");
        }, customExecutorService);

        CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);

        CompletableFuture.allOf(signal_1, signal_2)
                .thenApplyAsync(justVoid -> {
                     final int num = signal_2.join();
                     System.out.println(num);
                     return num;
                })
                .whenComplete((result, exception) -> {
                    if (exception != null) {
                        System.out.println("exception occurs");
                        System.err.println(exception);
                    } else {
                        System.out.println("no exception, got result: " + result);
                    }
                })
                .thenApplyAsync(input -> input * 3)
                .thenAccept(System.out::println);

    }

    public static void main(String[] args) {
        runTasks(0);
        customExecutorService.shutdown();
    }

}
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!

Process finished with exit code 0

共有1个答案

甘君之
2023-03-14

这在很大程度上是VGR在评论中所说的重复,但这是一个重要的经验法则,值得全面阐述。

在Java中,有一个重要的概念叫做“未指定行为”。简而言之,如果文档没有明确定义在特定场景中发生的情况,那么实现可以自由地在合理范围内和显式定义的其他规则的范围内执行它选择的任何内容。这很重要,因为有几种不同的表现形式。

首先,结果可能是平台特定的。对于某些机器,保留未定义的行为允许进行一些特殊优化,这些优化仍然会返回“正确”的结果。而且,由于Java优先考虑所有平台上的相似/相同行为以及性能,选择不指定执行的某些方面可以让它们信守promise,同时仍能获得特定平台带来的优化好处。

另一个例子是,将行为统一为特定行为的行为目前不可行。如果我不得不猜测,这很可能是Java实际上正在做的事情。在某些情况下,Java将设计一个具有某些功能潜力的组件,但不会实际定义和实现它。这通常是在构建一个完整的解决方案将比它的价值更多的情况下完成的,以及其他原因。具有讽刺意味的是,CompletableFuture本身就是一个很好的例子。Java 5引入了Future,但只是作为一个具有通用功能的接口。CompletableFuture是Java 8中出现的概念,后来充实并定义了Java 5接口遗留下来的所有未指定的行为。

最后,如果选择特定行为会扼杀可能实现的灵活性,他们可能会避免定义特定行为。目前,您展示的方法没有任何指定的行为,关于未来失败时将引发哪个异常。这允许以后扩展CompletableFuture的任何类能够为自己指定该行为,同时仍保持Liskov的替换原则。如果您还不知道,LSP说如果子类扩展父类,则该子类必须遵循父类的所有规则。因此,如果类的规则(指定的行为)过于严格,则会阻止该类的未来实现/扩展在不破坏LSP的情况下运行。CompletableFuture可能有一些扩展,允许您精确定义调用该方法时引发的异常类型。但这就是问题所在-它们是可以选择加入的扩展。如果它们为您定义了它,那么除非您自己实现它,或者您超出了语言标准库,否则您将无法使用它。

 类似资料:
  • 问题内容: 我想在操作完成后使用一个回调函数,我正在尝试如下操作: 问题在于,在操作完成之前会触发回调函数。 问题答案: 这是因为jQuery.when()需要jQuery.Deferred实例,而load()返回jQuery实例(请参见http://api.jquery.com/jQuery.when/和http://api.jquery.com/load/)。 您可以解决此问题:

  • 问题内容: 我通过将操作拆分为可用的确切内核数来并行化操作,然后通过启动相同数量的AsyncTask,对数据的不同部分执行相同的操作。 我正在使用以并行化它们的执行。 我想知道每个线程何时完成其工作,以便结合所有结果并执行进一步的操作。 我能怎么做? 问题答案: 您还可以简单地将共享库中的计数器递减作为的一部分。由于在同一线程(主线程)上运行,因此您不必担心同步。 更新1 共享对象可能看起来像这样

  • 问题内容: 使用此处给出的单个属性进行搜索时,自动完成功能可以正常工作。 通过->根据此,可以自动完成具有(名称,城市,国家/地区)等多个属性 但是,这会导致自动完成下拉列表/建议显示为“未定义”。 对于提前输入,我正在使用: 在代码中,它被引用为: 由于返回了一组以上的数据,因此在typeahead js文件中是否需要进行某些更改? 问题答案: 您需要返回一个 您在控制器中的操作应如下所示: 添

  • 问题内容: 我有一个循环,可以调用API并将结果编译成数组。我如何等待所有调用完成后才能恢复执行?我看到了一系列有关如何等到打完一个电话的答案,但我不知道如何检查所有这些。如果我做一个while循环,一直等到’obj’是正确的长度,则页面只会停顿直到调用完成,这不是我想要的。请帮助? 问题答案: 如果您使用jQuery的deferred,这很容易。有一种方法,等待多个诺言完成,然后运行回调。那就是

  • 我有下面的代码片段,我想用optionals重写 使用optionals,我可以想出以下方法。 我被困在无法根据用户名和电子邮件引发特定异常的部分。如果其中一个已经存在于db中,我可以返回null,这将导致orElseThrow工作,但异常类型相同。我想要两个不同情况的例外。我该怎么办?

  • 我正在运行这样的多个服务:(例如,在多个线程中读取文件) 是一个扩展类的类,它正在做一些事情,比如读取文件。它是从另一个线程调用的,该线程不是JavaFX应用程序线程。 我如何等到所有这些服务完成后再调用? 可复制的例子: