2017-02-27 6 views
5

我有3个CompletableFutures全部3个返回不同的数据类型。Java 8 CompletableFutures allOf不同的数据类型

我正在创建一个结果对象,它是由所有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; 
} 

我的问题是:

  1. 我在这里的假设是因为我用allOfthenAcceptAsync这个电话将是不可阻挡。我的理解是正确的吗?

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

  3. 是否正确构造ClassD对象在thenAcceptAsync内?

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

回答

6

您的尝试正在进入正确的方向,但不正确。你的方法getResultClassD()返回一个ClassD类型的已经实例化的对象,在这个对象上任意线程将调用修改方法,而不会调用getResultClassD()。如果修改方法本身不是线程安全的,则这可能会导致竞争条件,进一步说,当调用者实际准备好使用时,调用者永远不会知道。

一个正确的解决办法是:

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; 
     }); 
} 

现在,getResultClassD()调用者可以使用返回CompletableFuture查询进度状态或链相关的操作,或者使用join()检索结果,一旦操作完成。

为了解决其他问题,是的,这个操作是异步的,在lambda表达式中使用join()是合适的。 join的创建原因是Future.get(),它被声明为抛出检查异常,使得在这些lambda表达式中使用不必要的困难。

请注意,null测试只是有用的,如果这些service.getClassX()实际上可以返回null。如果其中一个服务调用失败且出现异常,则整个操作(由CompletableFuture<ClassD>表示)将异常完成。

+0

感谢您的详细回复。我唯一跟进的答案是,thenApplyAsync有一个返回类型CompletableFuture ,这将如何工作,以及如何调用此方法并使用结果 –

+4

不,它是'allOf'的返回类型,它是'CompletableFuture '这就是为什么通过'thenApplyAsync'的函数收到'Void'作为输入(上面的'dummy'参数,而不是'dummy - >',你也可以写'(void dummy) - >')。然后,该函数将'Void'输入(实际上忽略它)转换为'ClassD'结果,所以'thenApplyAsync'的结果将是'CompletableFuture '。 – Holger

+1

@Holger我正在沿着一条类似的路线,但是我在服务调用中使用了Optional.ofNullable,所以你可以有'cCFuture.join()。ifPresent(class :: SetStuff)' – Ash

3

我什么@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; 
    }); 
+1

是使用可选项是正确的方式来考虑它 –

0

另一种方式来处理如果你不想声明许多变量是使用thenCombine或thenCombineAsync来将你的期货连在一起的话。

public CompletableFuture<ClassD> getResultClassD() 
{ 
    return CompletableFuture.supplyAsync(ClassD::new) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassA), (d, a) -> { 
     d.setClassA(a); 
     return d; 
    }) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassB), (d, b) -> { 
     d.setClassB(b); 
     return d; 
    }) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassC), (d, c) -> { 
     d.setClassC(c); 
     return d; 
    }); 
} 

获取者仍然会被异步启动并按顺序执行结果。它基本上是获得相同结果的另一种语法选项。

+0

如果只有2或3期货合并,这可能是一个好方法。然而,你应该从'CompletableFuture.completedFuture(new ClassD())'开始,因为实例化可能不值得异步运行。事实上,你甚至可以在第一个将来的'thenApply()'中实例化它。 –