118

すべてのスレッド化されたプロセスが終了するのを単に待つ方法は何ですか? たとえば、私が持っているとしましょう:

public class DoSomethingInAThread implements Runnable{

    public static void main(String[] args) {
        for (int n=0; n<1000; n++) {
            Thread t = new Thread(new DoSomethingInAThread());
            t.start();
        }
        // wait for all threads' run() methods to complete before continuing
    }

    public void run() {
        // do something here
    }


}

これを変更して、すべてのスレッドのメソッドが終了main()するまでメソッドがコメントで一時停止するようにするにはどうすればよいですか? run()ありがとう!

4

14 に答える 14

165

すべてのスレッドを配列に入れ、それらをすべて開始してから、ループを作成します

for(i = 0; i < threads.length; i++)
  threads[i].join();

各結合は、それぞれのスレッドが完了するまでブロックされます。スレッドは、結合した順序とは異なる順序で完了する場合がありますが、それは問題ではありません。ループが終了すると、すべてのスレッドが完了します。

于 2009-08-09T20:31:37.307 に答える
42

1 つの方法はListThreadの を作成し、各スレッドを作成して起動し、それをリストに追加することです。すべてが起動したら、リストをループバックして、join()それぞれを呼び出します。スレッドがどの順序で実行を終了するかは問題ではありません。知っておく必要があるのは、2 番目のループの実行が終了するまでに、すべてのスレッドが完了するということだけです。

より良いアプローチは、ExecutorServiceとそれに関連するメソッドを使用することです。

List<Callable> callables = ... // assemble list of Callables here
                               // Like Runnable but can return a value
ExecutorService execSvc = Executors.newCachedThreadPool();
List<Future<?>> results = execSvc.invokeAll(callables);
// Note: You may not care about the return values, in which case don't
//       bother saving them

ExecutorService (および Java 5 の同時実行ユーティリティのすべての新しい機能) の使用は非常に柔軟で、上記の例は表面をかじっただけでもかまいません。

于 2009-08-09T20:31:55.013 に答える
28
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class DoSomethingInAThread implements Runnable
{
   public static void main(String[] args) throws ExecutionException, InterruptedException
   {
      //limit the number of actual threads
      int poolSize = 10;
      ExecutorService service = Executors.newFixedThreadPool(poolSize);
      List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();

      for (int n = 0; n < 1000; n++)
      {
         Future f = service.submit(new DoSomethingInAThread());
         futures.add(f);
      }

      // wait for all tasks to complete before continuing
      for (Future<Runnable> f : futures)
      {
         f.get();
      }

      //shut down the executor service so that this thread can exit
      service.shutdownNow();
   }

   public void run()
   {
      // do something here
   }
}
于 2009-08-10T00:25:01.303 に答える
12

古い API であるの代わりに、 CountDownLatchjoin()を使用できます。要件を満たすために、コードを次のように変更しました。

import java.util.concurrent.*;
class DoSomethingInAThread implements Runnable{
    CountDownLatch latch;
    public DoSomethingInAThread(CountDownLatch latch){
        this.latch = latch;
    } 
    public void run() {
        try{
            System.out.println("Do some thing");
            latch.countDown();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

public class CountDownLatchDemo {
    public static void main(String[] args) {
        try{
            CountDownLatch latch = new CountDownLatch(1000);
            for (int n=0; n<1000; n++) {
                Thread t = new Thread(new DoSomethingInAThread(latch));
                t.start();
            }
            latch.await();
            System.out.println("In Main thread after completion of 1000 threads");
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

説明:

  1. CountDownLatch要件に従って、指定されたカウント 1000 で初期化されています。

  2. 各ワーカー スレッドは、コンストラクターに渡された をDoSomethingInAThread デクリメントします。CountDownLatch

  3. CountDownLatchDemo await()カウントがゼロになるまでメインスレッド。カウントがゼロになると、出力が 1 行下になります。

    In Main thread after completion of 1000 threads
    

オラクルのドキュメントページからの詳細情報

public void await()
           throws InterruptedException

スレッドが中断されない限り、ラッチがゼロになるまで現在のスレッドを待機させます。

その他のオプションについては、関連する SE の質問を参照してください。

すべてのスレッドが Java での作業を完了するまで待ちます

于 2016-05-09T12:40:14.890 に答える
8

Threadクラスを完全に避け、代わりにjava.util.concurrentで提供されるより高度な抽象化を使用してください

ExecutorServiceクラスは、必要なことを実行しているように見えるメソッドinvokeAllを提供します。

于 2009-08-09T20:52:03.023 に答える
5

の使用を検討してくださいjava.util.concurrent.CountDownLatchjavadocの例

于 2014-11-13T15:14:22.283 に答える
4

必要に応じて、java.util.concurrent パッケージのクラス CountDownLatch および CyclicBarrier も確認することをお勧めします。スレッドが相互に待機するようにしたい場合や、スレッドの実行方法をよりきめ細かく制御したい場合 (たとえば、別のスレッドが何らかの状態を設定するのを内部実行で待機する場合) に便利です。ループを反復するときにスレッドを 1 つずつ開始するのではなく、CountDownLatch を使用してすべてのスレッドを同時に開始するように通知することもできます。標準 API ドキュメントにはこの例があり、さらに別の CountDownLatch を使用して、すべてのスレッドが実行を完了するのを待ちます。

于 2009-08-09T21:50:53.030 に答える
3

スレッドのリストを作成すると、それらをループして、それぞれに対して .join() を実行でき、すべてのスレッドが完了するとループが終了します。私はそれを試していません。

http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()

于 2009-08-09T20:31:00.350 に答える
0

問題:

for(i = 0; i < threads.length; i++)
  threads[i].join();

……というか、これthreads[i + 1]まで参加できなかった…… threads[i]。「ラッチ」されたものを除いて、すべてのソリューションにはこの欠如があります。

ここで(まだ)誰もExecutorCompletionServiceについて言及していません。完了順序に従ってスレッド/タスクを結合できます。

public class ExecutorCompletionService<V> extends Object implements CompletionService<V>

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

使用例。

特定の問題に対する一連のソルバーがあり、それぞれが何らかのタイプの Result の値を返し、それらを同時に実行して、null 以外の値を返すそれぞれの結果を何らかのメソッドで処理したいとしますuse(Result r)。これは次のように記述できます。

void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException, ExecutionException {
  CompletionService<Result> cs = new ExecutorCompletionService<>(e);
  solvers.forEach(cs::submit);
  for (int i = solvers.size(); i > 0; i--) {
    Result r = cs.take().get();
    if (r != null)
      use(r);
  }
}

代わりに、一連のタスクの最初の null 以外の結果を使用し、例外が発生したものは無視し、最初のタスクの準備ができたら他のすべてのタスクをキャンセルするとします。

void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException {
  CompletionService<Result> cs = new ExecutorCompletionService<>(e);
  int n = solvers.size();
  List<Future<Result>> futures = new ArrayList<>(n);
  Result result = null;
  try {
    solvers.forEach(solver -> futures.add(cs.submit(solver)));
    for (int i = n; i > 0; i--) {
      try {
        Result r = cs.take().get();
        if (r != null) {
          result = r;
          break;
        }
      } catch (ExecutionException ignore) {}
    }
  } finally {
    futures.forEach(future -> future.cancel(true));
  }

  if (result != null)
    use(result);
}

以降: 1.5 (!)

(例 1 の) 非同期も仮定するとuse(r)、大きな利点がありました。#

于 2021-12-22T21:06:04.990 に答える
0

オブジェクト「ThreadGroup」とそのパラメーター activeCount を使用して実行できます。

于 2009-08-09T20:27:47.207 に答える
0

CountDownLatchの代わりにCyclicBarrierを使用することもできます。

public class ThreadWaitEx {
    static CyclicBarrier barrier = new CyclicBarrier(100, new Runnable(){
        public void run(){
            System.out.println("clean up job after all tasks are done.");
        }
    });
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(new MyCallable(barrier));
            t.start();
        }       
    }

}    

class MyCallable implements Runnable{
    private CyclicBarrier b = null;
    public MyCallable(CyclicBarrier b){
        this.b = b;
    }
    @Override
    public void run(){
        try {
            //do something
            System.out.println(Thread.currentThread().getName()+" is waiting for barrier after completing his job.");
            b.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }       
}

この場合、CyclicBarrier を使用するには、barrier.await() を最後のステートメントにする必要があります。つまり、スレッドがジョブを完了したときです。CyclicBarrier は、その reset() メソッドで再び使用できます。javadoc を引用するには:

CyclicBarrier は、オプションの Runnable コマンドをサポートします。これは、パーティの最後のスレッドが到着した後、スレッドが解放される前に、バリア ポイントごとに 1 回実行されます。このバリア アクションは、いずれかの当事者が続行する前に共有状態を更新するのに役立ちます。

于 2016-09-16T16:12:59.943 に答える