2017-02-27 9 views
5

3つのCompletableFuturesがすべて3つの異なるデータ型を返します。Java 8完了可能先物すべて異なるデータ型

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ラムダでjoinまたはgetNowメソッドを使用するのが適切ですか?

答えて

6

あなたの試行は正しい方向に向いていますが、正しくありません。あなたのメソッドgetResultClassD()は、既に呼び出された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; 
     }); 
} 

次に、動作が完了すると、getResultClassD()の呼び出し側は、進捗状況やチェーン依存のアクションを照会したり、結果を取得するためにjoin()を使用するように戻っCompletableFutureを使用することができます。

は他の質問に対処するために、はい、この操作は非同期であり、ラムダ式内 join()の使用が適切です。チェック例外をスローすると宣言された Future.get()は、これらのラムダ式内での使用を不必要に困難にするため、 joinが正確に作成されました。

nullのテストは、service.getClassX()が実際にnullを返すことができる場合にのみ役立つことに注意してください。例外を伴ってサービスコールの1つが失敗すると、操作全体(CompletableFuture<ClassD>で表される)が例外的に完了します。

+0

詳細な返信をいただきありがとうございます。私の唯一の答えは、thenAppAppAsyncがCompletableFutureの戻り値のタイプを持っていて、これがここでどう機能し、どのようにこのメソッドを呼び出して結果を消費するのでしょうか? –

+4

いいえ、 'completetableFuture 'そのため、 'thenApplyAsync'関数を渡した関数が' Void'を入力として受け取ります(上記の 'dummy'パラメータの代わりに' dummy-> 'の代わりに'(Void dummy) - > 'を書くこともできます)。次に、関数は 'Void'入力を(実際には無視して)' ClassD'の結果に変換するので、 'thenApplyAsync'の結果は' CompletableFuture 'になります。 – Holger

+1

@Holger私はあなたと同様のルートを辿っていましたが、サービスコールでOptional.ofNullableを使用していましたので、 'cCFuture.join()。ifPresent(class :: SetStuff)'を使用できます。 – Ash

3

私は@Holgerが彼の答えでやっていたものと同様のルートを下って行くが、サービスを巻き付けて処理するための別の方法

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

yesオプションを使用することはそれを考える正しい方法です –

0

thenApplyAsync段階でクリーンなコードにつながる、オプションで呼び出しました多くの変数を宣言したくない場合は、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()'でインスタンス化することさえできます。 –

関連する問題