web-dev-qa-db-ja.com

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

私は、Webから同時に情報を取得し、バッファークラスの5つの異なるフィールドを埋める5つのスレッドを持つアプリケーションを作成しています。
バッファデータを検証し、すべてのスレッドがジョブを終了したときにデータベースに保存する必要があります。
どうすればこれを行うことができますか(すべてのスレッドが作業を終了したときに警告を受ける)?

79
RYN

私がとるアプローチは、 ExecutorService を使用してスレッドのプールを管理することです。

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
103
Peter Lawrey

join スレッドにできます。スレッドが完了するまで、結合はブロックされます。

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

joinInterruptedExceptionをスローすることに注意してください。その場合はどうするかを決める必要があります(たとえば、他のスレッドをキャンセルして、不要な作業が行われないようにします)。

45
Mark Byers

さまざまなソリューションをご覧ください。

  1. join() AP​​Iは、Javaの初期バージョンで導入されました。 JDK 1.5リリース以降、この concurrent パッケージでいくつかの優れた代替手段が利用可能です。

  2. ExecutorService#invokeAll()

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

    コード例については、次の関連するSEの質問を参照してください。

    すべてのスレッドプールにタスクを実行させるinvokeAll()の使用方法

  3. CountDownLatch

    他のスレッドで実行されている一連の操作が完了するまで、1つ以上のスレッドが待機できるようにする同期支援。

    CountDownLatchは、指定されたカウントで初期化されます。 awaitメソッドは、countDown()メソッドの呼び出しにより現在のカウントがゼロに達するまでブロックします。その後、すべての待機スレッドが解放され、その後のawaitの呼び出しはすぐに戻ります。これはワンショット現象です。カウントはリセットできません。カウントをリセットするバージョンが必要な場合は、CyclicBarrierの使用を検討してください。

    CountDownLatchの使用方法については、この質問を参照してください

    自身のスレッドを生成するスレッドを待つ方法

  4. ForkJoinPool または newWorkStealingPool() in Executors

  5. すべてを繰り返します FutureExecutorServiceへの送信後に作成されたオブジェクト

20
Ravindra babu

他の人によって提案されたThread.join()とは別に、Java 5はexecutorフレームワークを導入しました。そこではThreadオブジェクトを使用しません。代わりに、CallableまたはRunnableオブジェクトをエグゼキューターに送信します。複数のタスクを実行し、それらの結果を順不同で返すための特別なエグゼキューターがあります。それが ExecutorCompletionService です:

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

その後、Future<?>オブジェクトがなくなるまでtake()を繰り返し呼び出すことができます。つまり、すべてのオブジェクトが完了します。


シナリオに応じて、関連する可能性のあるもう1つのことは、 CyclicBarrier です。

スレッドセットがすべて共通のバリアポイントに到達するまで相互に待機できるようにする同期支援。 CyclicBarriersは、時々お互いを待たなければならないスレッドの固定サイズのパーティを含むプログラムで役立ちます。バリアは、待機中のスレッドが解放された後に再利用できるため、サイクリックと呼ばれます。

10
Bozho

別の可能性は CountDownLatch オブジェクトです。これは単純な状況に役立ちます。スレッドの数が事前にわかっているため、関連するカウントで初期化し、オブジェクトの参照を各スレッドに渡します。
タスクが完了すると、各スレッドはCountDownLatch.countDown()を呼び出し、内部カウンターをデクリメントします。メインスレッドは、他のすべてを開始した後、CountDownLatch.await()ブロッキング呼び出しを行う必要があります。内部カウンターが0に達するとすぐに解放されます。

このオブジェクトでは、InterruptedExceptionもスローできることに注意してください。

9
interDist

あなたがやる

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

このforループの後、すべてのスレッドがジョブを完了したことを確認できます。

8
aioobe

スレッドオブジェクトをコレクション(リストやセットなど)に保存し、スレッドが開始されたらコレクションをループし、スレッドで join() を呼び出します。

4
esaj

OPの問題には関係ありませんが、ちょうど1つのスレッドとの同期(より正確にはランデブー)に関心がある場合は、 Exchanger を使用できます

私の場合、子スレッドが何かをするまで親スレッドを一時停止する必要がありました。初期化を完了しました。 CountDownLatch もうまく機能します。

この目的で Thread#join メソッドを使用できます。

2
Narendra Yadala

同様の問題があり、Java 8 parallelStreamを使用することになりました。

requestList.parallelStream().forEach(req -> makeRequest(req));

とてもシンプルで読みやすいです。舞台裏では、デフォルトのJVMのフォーク結合プールを使用しています。つまり、続行する前にすべてのスレッドが終了するまで待機します。私の場合、これはアプリケーション内の唯一のparallelStreamであるため、適切なソリューションでした。同時に複数のparallelStreamを実行している場合は、以下のリンクを読んでください。

並列ストリームに関する詳細情報 こちら

1
Madis Pukkonen

これを試して、動作します。

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }
1
Jeyaraj.J

他のスレッドが作業を完了するまで、スレッドメインを待機/ブロックします。

@Ravindra babuが言ったように、それはさまざまな方法で達成できますが、例を示しています。

  • Java.lang.Thread . join() から:1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • Java.util.concurrent.CountDownLatch から:1.5

    • .countDown()"ラッチグループのカウントをデクリメントします。
    • .await()"awaitメソッドは、現在のカウントがゼロに達するまでブロックします。

    latchGroupCount = 4を作成した場合、countDown()を4回呼び出してカウントを0にする必要があります。したがって、そのawait()はブロッキングスレッドを解放します。

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

スレッドクラスLatchTaskのサンプルコード。アプローチをテストするには、mainメソッドのjoiningThreads();およびlatchThreads();を使用します。

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • CyclicBarriers スレッドのセットがすべて共通のバリアポイントに到達するまで相互に待機することを許可する同期エイド。バリアは、待機中のスレッドが解放された後に再利用できるため、循環と呼ばれます。
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    
    たとえば、これを参照してください Concurrent_ParallelNotifyies class。

  • Executer framework: ExecutorService を使用してスレッドプールを作成し、Futureで非同期タスクの進行状況を追跡できます。

    • Future Objectを返すsubmit(Runnable)submit(Callable)future.get()関数を使用することにより、作業スレッドが作業を完了するまでメインスレッドをブロックできます。

    • invokeAll(...)-各Callableの実行結果を取得できるFutureオブジェクトのリストを返します。

例の検索 実行可能インターフェイス、Executorフレームワークを使用した呼び出し可能インターフェイスの使用。


@も参照

1
Yash

Executorサービスを使用して、ステータスや完了などの複数のスレッドを管理できます。 http://programmingexamples.wikidot.com/executorservice を参照してください

1
user974465

いくつかのスレッドが終了するのを待つ小さなヘルパーメソッドを作成しました。

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
0
Alon Gouldman

既存の答えは、各スレッドをjoin()できると言っていました。

ただし、スレッド配列/リストを取得する方法はいくつかあります。

  • 作成時にリストにスレッドを追加します。
  • ThreadGroupを使用して、スレッドを管理します。

次のコードでは、ThreadGruopアプローチを使用します。最初にグループを作成し、次に各スレッドを作成するときにコンストラクタでグループを指定すると、後でThreadGroup.enumerate()を介してスレッド配列を取得できます


コード

SyncBlockLearn.Java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

メインスレッドは、グループ内のすべてのスレッドが終了するまで待機します。

0
Eric Wang