いくつかのスレッドが完了するのを待つ方法は?


109

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

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()メソッドが終了するまでメソッドがコメントで一時停止するようにするにはどうすればよいですか?ありがとう!

回答:


163

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

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

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


1
@Mykola:どのような正確にスレッドグループを使用する利点はありますか?APIがあるからといって、それを使用する必要があるわけではありません...
Martin v。LöwisAug

2
参照:「スレッドグループはスレッドのセットを表します。」これは、このユースケースの意味論的に正しいです!そして:「スレッドはそれ自身のスレッドグループに関する情報へのアクセスを許可されています」
Martin K.

4
「Effective Java」という本は、スレッドグループを回避することを推奨しています(項目73)。
バスティアンレオナール

2
効果的なJavaで言及されたバグはJava 6で修正されているはずです。新しいJavaバージョンが制限事項でない場合は、Futuresを使用してスレッドの問題を解決することをお勧めします。マーティン対ロイス:その通りです。その問題には関係ありませんが、1つのオブジェクト(ExecutorServiceなど)から実行中のスレッドに関する詳細情報を取得すると便利です。特定の機能を使用して問題を解決するのは良いことだと思います。将来的には、より多くの柔軟性(スレッド情報)が必要になるでしょう。古いJDKの古いバグのあるクラスについて言及するのも正しいことです。
マーティンK.

5
ThreadGroupはグループレベルの結合を実装していないため、人々がThreadGroupをプッシュしている理由は少し困惑しています。人々は本当にスピンロックを使用していて、グループのactiveCountをクエリしていますか?すべてのスレッドでjoinを呼び出すだけの場合と比較すると、そうする方が良いと私に納得させるのは難しいでしょう。

41

1つの方法はListThreadsを作成し、リストに追加しながら各スレッドを作成して起動することです。すべてが起動したら、リストをループバックして、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の同時実行ユーティリティのすべての新機能)の使用は非常に柔軟であり、上記の例では表面をかろうじて引っ掻くことさえほとんどありません。


ThreadGroupを使用する方法です。変更可能なリストを使用すると、問題が発生します(同期)
Martin K.

3
何?どのように問題が発生しますか?これは、起動を実行しているスレッドによってのみ変更可能(読み取り可能のみ)なので反復処理にリスト変更しない限り問題ありません。
アダムバットキン2009

使い方次第です。スレッドで呼び出しクラスを使用する場合、問題が発生します。
マーティンK.

27
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
   }
}

チャームのように動作しました...複数のCookieの問題のため、同時に実行してはならない2つのスレッドセットがあります。私はあなたの例を使用して、一度に1セットのスレッドを実行しました。知識を共有してくれてありがとう...
arn-arn

@Dantalian-Runnableクラス(おそらくrunメソッド内)で、発生した例外をキャプチャしてローカルに保存(またはエラーメッセージ/条件を保存)します。この例では、f.get()は、ExecutorServiceに送信したオブジェクトを返します。オブジェクトには、例外/エラー状態を取得するためのメソッドを含めることができます。提供されている例をどのように変更するかによっては、f.get()によって変換されたオブジェクトを期待されるタイプにキャストする必要がある場合があります。
jt。

12

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

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. 各ワーカースレッドDoSomethingInAThreadCountDownLatch、コンストラクタで渡された。

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

    In Main thread after completion of 1000 threads

Oracleのドキュメントページの詳細

public void await()
           throws InterruptedException

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

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

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



6

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


スレッドのラッチです。ラッチロックはカウントダウンで機能します。スレッドのrun()メソッドで、CountDownLatchがそのカウントダウンが0に達するのを待つことを明示的に宣言します。複数のスレッドで同じCountDownLatchを使用して、それらを同時に解放できます。それがあなたが必要とするものかどうかはわかりませんが、マルチスレッド環境で作業するときに役立つので、それを述べたかっただけです。
Pablo Cavalieri

多分あなたはあなたの答えの本文にその説明を入れるべきですか?
アーロンホール

Javadocの例は非常にわかりやすいため、何も追加しませんでした。docs.oracle.com/javase/7/docs/api/java/util/concurrent/…。最初の例では、すべてのワーカースレッドが同時にリリースされます。これは、startSignal.countDown()で発生するCountdownLatch startSignalがゼロに達するのを待つためです。次に、mianスレッドは、doneSignal.await()命令を使用してすべての作業が完了するまで待機します。doneSignalは、各ワーカーでその値を減らします。
Pablo Cavalieri

6

マーティンKが提案した java.util.concurrent.CountDownLatchように、これにはより良い解決策のようです。同じの例を追加するだけ

     public class CountDownLatchDemo
{

    public static void main (String[] args)
    {
        int noOfThreads = 5;
        // Declare the count down latch based on the number of threads you need
        // to wait on
        final CountDownLatch executionCompleted = new CountDownLatch(noOfThreads);
        for (int i = 0; i < noOfThreads; i++)
        {
            new Thread()
            {

                @Override
                public void run ()
                {

                    System.out.println("I am executed by :" + Thread.currentThread().getName());
                    try
                    {
                        // Dummy sleep
                        Thread.sleep(3000);
                        // One thread has completed its job
                        executionCompleted.countDown();
                    }
                    catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }.start();
        }

        try
        {
            // Wait till the count down latch opens.In the given case till five
            // times countDown method is invoked
            executionCompleted.await();
            System.out.println("All over");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

}

4

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


3

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

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


こんにちは、なぜか私にはうまくいきませんでした。これが私の質問です:stackoverflow.com/users/5144855/ruchir-baronia
Ruchir Baronia

1

最初のforループ内にスレッドオブジェクトを作成します。

for (int i = 0; i < threads.length; i++) {
     threads[i] = new Thread(new Runnable() {
         public void run() {
             // some code to run in parallel
         }
     });
     threads[i].start();
 }

それで、ここのみんなが言っていること。

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

0

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


どのようにそれを行うことを正確に提案するかわかりません。ループでactiveCountをポーリングすることを提案した場合、ビジー待機であるため、悪いことです(ポーリング間でスリープした場合でも、ビジネスと応答性のトレードオフが発生します)。
Martin v。Löwis、2009

@Martin v。Löwis: "Joinは単一のスレッドのみを待機します。より良い解決策は、java.util.concurrent.CountDownLatchです。カウントをワーカースレッドの数に設定してラッチを初期化するだけです。各ワーカースレッドは、終了する直前のcountDown()、およびメインスレッドが単にawait()を呼び出すと、カウンタがゼロになるまでブロックされます。join()の問題は、動的にスレッドを追加し始めることができないことです。リストが爆発します。同時に変更します。」あなたの解決策は問題に対してはうまくいきますが、一般的な目的に対してはうまくいきません。
マーティンK.

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()メソッドで再び使用できます。javadocsを引用するには:

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


これはCyclicBarrierの良い例ではないと思います。なぜThread.sleep()コールを使用するのですか?
Guenther

@ギュンター-はい、私は要件に合うようにコードを変更しました。
shailendra1118 2016

CyclicBarrierはCountDownLatchに代わるものではありません。スレッドが繰り返しカウントダウンする必要がある場合は、CyclicBarrierを作成する必要があります。それ以外の場合は、デフォルトでCountDownLatchを使用します(実行の追加の抽象化が必要でない場合は、より高いレベルのサービスを確認する必要があります)。
Elysiumplain 2017年

0

join()私には有用ではなかったです。Kotlinでこのサンプルを参照してください。

    val timeInMillis = System.currentTimeMillis()
    ThreadUtils.startNewThread(Runnable {
        for (i in 1..5) {
            val t = Thread(Runnable {
                Thread.sleep(50)
                var a = i
                kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
                Thread.sleep(200)
                for (j in 1..5) {
                    a *= j
                    Thread.sleep(100)
                    kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
                }
                kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
            })
            t.start()
        }
    })

結果:

Thread-5|a=5
Thread-1|a=1
Thread-3|a=3
Thread-2|a=2
Thread-4|a=4
Thread-2|2*1=2
Thread-3|3*1=3
Thread-1|1*1=1
Thread-5|5*1=5
Thread-4|4*1=4
Thread-1|2*2=2
Thread-5|10*2=10
Thread-3|6*2=6
Thread-4|8*2=8
Thread-2|4*2=4
Thread-3|18*3=18
Thread-1|6*3=6
Thread-5|30*3=30
Thread-2|12*3=12
Thread-4|24*3=24
Thread-4|96*4=96
Thread-2|48*4=48
Thread-5|120*4=120
Thread-1|24*4=24
Thread-3|72*4=72
Thread-5|600*5=600
Thread-4|480*5=480
Thread-3|360*5=360
Thread-1|120*5=120
Thread-2|240*5=240
Thread-1|TaskDurationInMillis = 765
Thread-3|TaskDurationInMillis = 765
Thread-4|TaskDurationInMillis = 765
Thread-5|TaskDurationInMillis = 765
Thread-2|TaskDurationInMillis = 765

join()forスレッドを使用してみましょう:

    val timeInMillis = System.currentTimeMillis()
    ThreadUtils.startNewThread(Runnable {
        for (i in 1..5) {
            val t = Thread(Runnable {
                Thread.sleep(50)
                var a = i
                kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
                Thread.sleep(200)
                for (j in 1..5) {
                    a *= j
                    Thread.sleep(100)
                    kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
                }
                kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
            })
            t.start()
            t.join()
        }
    })

そしてその結果:

Thread-1|a=1
Thread-1|1*1=1
Thread-1|2*2=2
Thread-1|6*3=6
Thread-1|24*4=24
Thread-1|120*5=120
Thread-1|TaskDurationInMillis = 815
Thread-2|a=2
Thread-2|2*1=2
Thread-2|4*2=4
Thread-2|12*3=12
Thread-2|48*4=48
Thread-2|240*5=240
Thread-2|TaskDurationInMillis = 1568
Thread-3|a=3
Thread-3|3*1=3
Thread-3|6*2=6
Thread-3|18*3=18
Thread-3|72*4=72
Thread-3|360*5=360
Thread-3|TaskDurationInMillis = 2323
Thread-4|a=4
Thread-4|4*1=4
Thread-4|8*2=8
Thread-4|24*3=24
Thread-4|96*4=96
Thread-4|480*5=480
Thread-4|TaskDurationInMillis = 3078
Thread-5|a=5
Thread-5|5*1=5
Thread-5|10*2=10
Thread-5|30*3=30
Thread-5|120*4=120
Thread-5|600*5=600
Thread-5|TaskDurationInMillis = 3833

いつ使用するかは明らかjoinです。

  1. スレッドは順次実行されています。
  2. 最初のサンプルは765ミリ秒かかり、2番目のサンプルは3833ミリ秒かかります。

他のスレッドのブロックを防ぐためのソリューションは、ArrayListを作成することでした。

val threads = ArrayList<Thread>()

ここで、新しいスレッドを開始する場合は、ほとんどの場合、それをArrayListに追加します。

addThreadToArray(
    ThreadUtils.startNewThread(Runnable {
        ...
    })
)

addThreadToArray機能:

@Synchronized
fun addThreadToArray(th: Thread) {
    threads.add(th)
}

startNewThreadfunstion:

fun startNewThread(runnable: Runnable) : Thread {
    val th = Thread(runnable)
    th.isDaemon = false
    th.priority = Thread.MAX_PRIORITY
    th.start()
    return th
}

必要に応じて、以下のようにスレッドの完了を確認してください。

val notAliveThreads = ArrayList<Thread>()
for (t in threads)
    if (!t.isAlive)
        notAliveThreads.add(t)
threads.removeAll(notAliveThreads)
if (threads.size == 0){
    // The size is 0 -> there is no alive threads.
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.