web-dev-qa-db-ja.com

Completableの将来のスレッドが終了するまで待つための推奨される方法は何ですか

以下のコードで示すように、CompletableFutureを使用しています。しかし、すべての実行可能ファイルが終了するまで待つ方法については、2つの方法を見つけましたが、それらの違いとどちらがベストプラクティスであるかがわかりません。それらは次のとおりです。

コード

this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);

すべての実行可能ファイルが終了するまで待機する最初のアプローチ

this.growSeedExecutor.shutdown();
this.growSeedExecutor.awaitTermination(1, TimeUnit.DAYS);

すべての実行可能ファイルが終了するまで待機する2番目のアプローチ

CompletableFuture.allOf(this.growSeedFutureList).join();

どれが推奨されるか教えてください。

20
user2121

両方の方法は、エグゼキュータ(growSeedExecutor)が特定のタスクにのみ使用される場合にのみ同等です。最初の方法は次のようになる可能性があります。別のタスクには並列化が必要であり、タスクごとに新しいエクゼキューターが作成されます。一部の開発者は、作成されたエグゼキューターの数が多すぎて、単一の共通エグゼキューターを使用することに決めましたが、すべてのエグゼキューターのシャットダウンを削除できませんでした...

したがって、2番目の方法(join())は、それほど複雑ではないため、より信頼性が高くなります。ただし、新しい未来はそれぞれ、growSeedFutureListに追加する必要があり、割り当てられません。

8

すべての先物を本当に待ちたい場合は、それぞれの先物でjoin()を呼び出すだけです:

_growSeedFutureList.forEach(CompletableFuture::join);
_

allOf()を使用することと比較した主な違いは、例外が完了したfutureに到達するとすぐに例外がスローされるのに対して、allOf().join()バージョンはすべてのfutureの後のみ例外をスローすることです。完了した(例外的に、またはそうでない)。

もう1つの小さな違いは、これにより中間allOfステージが作成されないことです。そのようなステージは、すべてのフューチャーが完了するのを待つのではなく、すべてのフューチャーが完了した後に何かを非同期的に実行する場合に役立ちます。

反対側のエグゼキューターを使用したソリューションには、いくつかの欠点があります。

  • シャットダウンが必要なため、エグゼキューターを再利用できません。
  • すべての操作でそのエグゼキューターを使用する必要があります。別の方法で管理されているCompletableFuturesでは機能しません。
  • すべての先物が完了するのを待つという意図を明確に示していません。
  • 実装はより複雑です。
  • 例外的な完了は処理しません。タスクの1つが失敗した場合、awaitTermination()によって例外はスローされません。
10
Didier L