web-dev-qa-db-ja.com

invokeAllを使用するか、submitを使用するか-Java Executorサービス

同じ呼び出し可能オブジェクトに対して5つのスレッドを非同期で実行する必要があるシナリオがあります。私が理解している限り、2つのオプションがあります。

1)submit(Callable)を使用する

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for(Callable callableItem: myCallableList){
    futures.add(executorService.submit(callableItem));
}

2)invokeAll(Callablesのコレクション)を使用する

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));
  1. 何が望ましい方法でしょうか?
  2. 他のものと比較して、それらのいずれかに不利な点やパフォーマンスへの影響はありますか?
17
Ankit

オプション1:タスクをExecutorServiceに送信し、送信されたすべてのタスクの完了を待機していませんExecutorService

オプション2ExecutorServiceに送信されたすべてのタスクの完了を待っています。

何が望ましい方法でしょうか?

アプリケーションの要件に応じて、どちらかが推奨されます。

  1. タスクsubmit()がExecutorServiceになってから待機したくない場合は、Option 1
  2. ExecutorServiceに送信されたすべてのタスクの完了を待つ必要がある場合は、Option 2

他のものと比較して、それらのいずれかに不利な点やパフォーマンスへの影響はありますか?

アプリケーションがオプション2を要求する場合、オプション1とは異なり、ExecutorServiceに送信されたすべてのタスクが完了するまで待つ必要があります。パフォーマンスは、両方が2つの異なる目的のために設計されているため、比較の基準ではありません。

そしてもう1つの重要なこと:どちらのオプションでも、FutureTaskはタスクの実行中に例外を飲み込みます。あなたは注意する必要があります。このSEの質問を見てください: ThreadPoolExecutorの例外の処理

Java 8の場合、もう1つのオプションがあります: ExecutorCompletionService

提供されたエグゼキューターを使用してタスクを実行するCompletionService。このクラスは、送信されたタスクが完了時に、takeを使用してアクセス可能なキューに配置されるように調整します。このクラスは軽量で、タスクのグループを処理する際の一時的な使用に適しています。

関連するSEの質問を見てください: ExecutorCompletionService?invokeAllがあるのになぜ必要なのですか?

16
Ravindra babu

編集:

実際には違いがあります。何らかの理由で、invokeAll()は、生成されるfutureごとにget()を呼び出します。したがって、タスクが完了するまで待機するため、InterruptedExceptionがスローされる可能性があります(submit()は何もスローしません)。

これがinvokeAll()メソッドのJavadocです。

指定されたタスクを実行し、ステータスと結果を保持するFutureのリストを返しますすべて完了したとき

したがって、どちらの戦略も基本的に同じですが、invokeAll()を呼び出すと、すべてのタスクが完了するまでブロックされます。


元の(不完全な)回答:

invokeAll()メソッドは、このような状況にぴったりです。あなたは間違いなくそれを使うべきです。

ただし、実際にListをインスタンス化する必要はありません。

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));

これで十分なはずですし、最初の選択肢よりもずっときれいに見えます。

3

独立して実行可能なタスクの数に結果が依存するタスクがあるとします。しかし、最初のタスクを完了するには、限られた時間しかありません。そのAPI呼び出しのように。

したがって、たとえば、最上位のタスクが完了するまでに100ミリ秒かかり、10個の依存タスクもあります。そのため、ここで送信を使用している場合、コードは次のようになります。

List<Callable> tasks = []// assume contains sub tasks
List<Future> futures = [] 
for(Callable task: tasks) {
   futures.add(service.submit(task));
}

for(Future futute: futures) {
    future.get(100, TimeUnit.MILLISECONDS);
}

したがって、各サブタスクが上記のコードを完了するのに50ミリ秒かかった場合、50ミリ秒かかります。ただし、各サブタスクに1000ミリ秒かかる場合、上記の処理には100 * 10 = 1000ミリ秒または1秒かかります。これにより、すべてのサブタスクの合計時間を100ミリ秒未満に計算することが難しくなっています。

invokeAllメソッドは、このようなシナリオで私たちを助けます

List<Futures> futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS)
for(Future future: futures) {
   if(!future.isCancelled()) {
       results.add(future.get());
   }
}

この方法では、サブタスクの個々のタスクがそれより多くかかった場合でも、かかる最大時間は100ミリ秒以内です。

1
parampreet bawa