web-dev-qa-db-ja.com

Javaで複数のスレッドが完了するのを待機しています

プログラムの実行中に、いくつかのスレッドが開始されます。スレッドの量は、ユーザー定義の設定によって異なりますが、すべて異なる変数を使用して同じメソッドを実行しています。

いくつかの状況では、実行中にクリーンアップが必要です。これの一部はすべてのスレッドを停止することです。ただ、それらをすぐに停止させたくはありません。問題は、スレッドが停止するまでに最大1/2秒かかる場合があることです。ただし、クリーンアップを続行するには、すべてのスレッドが停止していることを確認する必要があります。クリーンアップは別のスレッドから実行されるため、技術的にはこのスレッドが他のスレッドの終了を待つ必要があります。

私はこれを行ういくつかの方法を考えましたが、それらはすべて過度に複雑に思えます。スレッドのグループが完了するのを待つことができる何らかの方法があることを望んでいました。このようなものはありますか?

ありがとう。

35
A Jackson

ひとつずつ参加してください:

for (Thread thread : threads) {
  thread.join();
}

InterruptedExceptionを使用して何かを行う必要があります。問題が発生した場合にタイムアウトを提供することもできますが、それが基本的な考え方です...)

58
Jon Skeet

Java 1.5以降)を使用している場合は、 CyclicBarrier を試すことができます。クリーンアップ操作をコンストラクターパラメーターとして渡し、barrier.await()を呼び出すだけです。クリーンアップが必要なすべてのスレッドで。

14
Tadeusz Kopec

Java.util.concurrentExecutorクラスを見ましたか? ExecutorServiceを介してスレッドを実行できます。スレッドをキャンセルするか、スレッドが完了するのを待つために使用できる単一のオブジェクトを提供します。

9
Kenster

自分でユーティリティメソッドを定義します。

public static waitFor(Collection<? extends Thread) c) throws InterruptedException {
    for(Thread t : c) t.join();
}

または、配列がある場合があります

public static waitFor(Thread[] ts) throws InterruptedException {
    waitFor(Arrays.asList(ts));
}

あるいは、Java.util.concurrentライブラリーのCyclicBarrierを使用して、複数のスレッド間で任意のrendezvousポイントを実装することもできます。

8
oxbow_lakes

スレッドの作成(ExecutorServiceへの送信)を制御する場合、ExecutorCompletionServiceを使用できるようです ExecutorCompletionService? invokeAllがある場合、なぜ必要なのですか? そこにさまざまな答えがあります。

スレッドの作成を制御しない場合は、Rubyに触発されて、「1つずつ終了する」スレッドを結合する(そして、どのスレッドが最初に終了するかなどを知る)ことができるアプローチがあります ThreadWait クラス。基本的に、他のスレッドが終了したときに警告する「監視スレッド」を更新することにより、多くの「次の」スレッドが終了したことを知ることができます。

次のようなものを使用します。

JoinThreads join = new JoinThreads(threads);
for(int i = 0; i < threads.size(); i++) {
  Thread justJoined = join.joinNextThread();
  System.out.println("Done with a thread, just joined=" + justJoined);
}

そしてソース:

public static class JoinThreads {
  Java.util.concurrent.LinkedBlockingQueue<Thread> doneThreads = 
      new LinkedBlockingQueue<Thread>();

  public JoinThreads(List<Thread> threads) {
    for(Thread t : threads) {
      final Thread joinThis = t;
      new Thread(new Runnable() {
        @Override
        public void run() {
          try {
            joinThis.join();
            doneThreads.add(joinThis);
          }
          catch (InterruptedException e) {
            // "should" never get here, since we control this thread and don't call interrupt on it
          }
        }
      }).start();
    }

  }

  Thread joinNextThread() throws InterruptedException {
    return doneThreads.take();
  }
}

これの良いところは、一般的なJavaスレッドで動作することです。変更することなく、任意のスレッドを結合できます。警告は、いくつかの追加のスレッド作成が必要です。また、この特定の実装は、joinNextThread()を何回も呼び出さず、「close」メソッドなどを持たない場合、「スレッドを残します」。より洗練されたバージョンを作成したい場合は、ここにコメントしてください。スレッドオブジェクトなどの代わりに、「未来」でこの同じタイプのパターンを使用することもできます。

1
rogerdpack