Java 8 Completable Futures allOf不同的数据类型

Ana*_*man 9 java-8 completable-future

我有3个CompletableFutures,所有3个返回不同的数据类型.

我希望创建一个结果对象,它是所有3个期货返回的结果的组合.

所以我目前的工作代码如下所示:

public ClassD getResultClassD() {

    ClassD resultClass = new ClassD();
    CompletableFuture<ClassA> classAFuture = CompletableFuture.supplyAsync(() -> service.getClassA() );
    CompletableFuture<ClassB> classBFuture = CompletableFuture.supplyAsync(() -> service.getClassB() );
    CompletableFuture<ClassC> classCFuture = CompletableFuture.supplyAsync(() -> service.getClassC() );

    CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
                     .thenAcceptAsync(it -> {
                        ClassA classA = classAFuture.join();
                        if (classA != null) {
                            resultClass.setClassA(classA);
                        }

                        ClassB classB = classBFuture.join();
                        if (classB != null) {
                            resultClass.setClassB(classB);
                        }

                        ClassC classC = classCFuture.join();
                        if (classC != null) {
                            resultClass.setClassC(classC);
                        }

                     });

    return resultClass;
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 我的假设是,因为我正在使用allOf,thenAcceptAsync 这个调用将是非阻塞的.我的理解是对的吗?

  2. 这是处理返回不同结果类型的多个期货的正确方法吗?

  3. 在其中构建ClassD对象是正确的thenAcceptAsync吗?

  4. 是否适合在thenAcceptAsync lambda中使用joinor getNow方法?

Hol*_*ger 10

你的尝试正朝着正确的方向发展,但不正确.您的方法getResultClassD()返回一个已经实例化的类型对象,ClassD任意线程将在该对象上调用修改方法,而不会给调用者getResultClassD()注意.这可能会导致竞争条件,如果修改方法本身不是线程安全的,那么调用者将永远不知道ClassD实例何时可以使用.

一个正确的解决方案是:

public CompletableFuture<ClassD> getResultClassD() {

    CompletableFuture<ClassA> classAFuture
        = CompletableFuture.supplyAsync(() -> service.getClassA() );
    CompletableFuture<ClassB> classBFuture
        = CompletableFuture.supplyAsync(() -> service.getClassB() );
    CompletableFuture<ClassC> classCFuture
        = CompletableFuture.supplyAsync(() -> service.getClassC() );

    return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
         .thenApplyAsync(dummy -> {
            ClassD resultClass = new ClassD();

            ClassA classA = classAFuture.join();
            if (classA != null) {
                resultClass.setClassA(classA);
            }

            ClassB classB = classBFuture.join();
            if (classB != null) {
                resultClass.setClassB(classB);
            }

            ClassC classC = classCFuture.join();
            if (classC != null) {
                resultClass.setClassC(classC);
            }

            return resultClass;
         });
}
Run Code Online (Sandbox Code Playgroud)

现在,调用者getResultClassD()可以使用返回CompletableFuture来查询进度状态或链依赖操作,或者join()在操作完成后用于检索结果.

为解决其他问题,是的,此操作是异步的,并且join()在lambda表达式中使用是合适的.join是完全创建的,因为Future.get(),声明抛出已检查的异常,使得在这些lambda表达式中的使用不必要地变硬.

请注意,null测试仅在service.getClassX()实际返回时才有用null.如果其中一个服务调用因异常而失败,则整个操作(由表示CompletableFuture<ClassD>)将异常完成.

  • 不,它是`allOf`的返回类型,它是`CompletableFuture <Void>`,这就是为什么传递`thenApplyAsync`的函数接收`Void`作为输入(上面的`dummy`参数,而不是`dummy - >`,你也可以写`(Void dummy) - >`).然后,该函数将`Void`输入(通过实际忽略它)转换为`ClassD`结果,因此`thenApplyAsync`的结果将是`CompletableFuture <ClassD>`. (4认同)

Ash*_*Ash 5

我走的路线与@Holger 在他的回答中所做的类似,但将服务调用包装在一个可选中,这会导致 thenApplyAsync 阶段中的代码更清晰

CompletableFuture<Optional<ClassA>> classAFuture
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassA())));

CompletableFuture<Optional<ClassB>> classBFuture
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassB()));

CompletableFuture<Optional<ClassC>> classCFuture
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassC()));

return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
     .thenApplyAsync(dummy -> {
        ClassD resultClass = new ClassD();

        classAFuture.join().ifPresent(resultClass::setClassA)
        classBFuture.join().ifPresent(resultClass::setClassB)
        classCFuture.join().ifPresent(resultClass::setClassC)

        return resultClass;
     });
Run Code Online (Sandbox Code Playgroud)