私は、Webから同時に情報を取得し、バッファークラスの5つの異なるフィールドを埋める5つのスレッドを持つアプリケーションを作成しています。
バッファデータを検証し、すべてのスレッドがジョブを終了したときにデータベースに保存する必要があります。
どうすればこれを行うことができますか(すべてのスレッドが作業を終了したときに警告を受ける)?
私がとるアプローチは、 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.
join
スレッドにできます。スレッドが完了するまで、結合はブロックされます。
for (Thread thread : threads) {
thread.join();
}
join
はInterruptedException
をスローすることに注意してください。その場合はどうするかを決める必要があります(たとえば、他のスレッドをキャンセルして、不要な作業が行われないようにします)。
さまざまなソリューションをご覧ください。
join()
APIは、Javaの初期バージョンで導入されました。 JDK 1.5リリース以降、この concurrent パッケージでいくつかの優れた代替手段が利用可能です。
指定されたタスクを実行し、すべてが完了したときにステータスと結果を保持するFutureのリストを返します。
コード例については、次の関連するSEの質問を参照してください。
他のスレッドで実行されている一連の操作が完了するまで、1つ以上のスレッドが待機できるようにする同期支援。
CountDownLatchは、指定されたカウントで初期化されます。 awaitメソッドは、
countDown()
メソッドの呼び出しにより現在のカウントがゼロに達するまでブロックします。その後、すべての待機スレッドが解放され、その後のawaitの呼び出しはすぐに戻ります。これはワンショット現象です。カウントはリセットできません。カウントをリセットするバージョンが必要な場合は、CyclicBarrierの使用を検討してください。
CountDownLatch
の使用方法については、この質問を参照してください
すべてを繰り返します FutureExecutorService
への送信後に作成されたオブジェクト
他の人によって提案されたThread.join()
とは別に、Java 5はexecutorフレームワークを導入しました。そこではThread
オブジェクトを使用しません。代わりに、Callable
またはRunnable
オブジェクトをエグゼキューターに送信します。複数のタスクを実行し、それらの結果を順不同で返すための特別なエグゼキューターがあります。それが ExecutorCompletionService
です:
ExecutorCompletionService executor;
for (..) {
executor.submit(Executors.callable(yourRunnable));
}
その後、Future<?>
オブジェクトがなくなるまでtake()
を繰り返し呼び出すことができます。つまり、すべてのオブジェクトが完了します。
シナリオに応じて、関連する可能性のあるもう1つのことは、 CyclicBarrier
です。
スレッドセットがすべて共通のバリアポイントに到達するまで相互に待機できるようにする同期支援。 CyclicBarriersは、時々お互いを待たなければならないスレッドの固定サイズのパーティを含むプログラムで役立ちます。バリアは、待機中のスレッドが解放された後に再利用できるため、サイクリックと呼ばれます。
別の可能性は CountDownLatch
オブジェクトです。これは単純な状況に役立ちます。スレッドの数が事前にわかっているため、関連するカウントで初期化し、オブジェクトの参照を各スレッドに渡します。
タスクが完了すると、各スレッドはCountDownLatch.countDown()
を呼び出し、内部カウンターをデクリメントします。メインスレッドは、他のすべてを開始した後、CountDownLatch.await()
ブロッキング呼び出しを行う必要があります。内部カウンターが0に達するとすぐに解放されます。
このオブジェクトでは、InterruptedException
もスローできることに注意してください。
あなたがやる
for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
t.join()
このforループの後、すべてのスレッドがジョブを完了したことを確認できます。
スレッドオブジェクトをコレクション(リストやセットなど)に保存し、スレッドが開始されたらコレクションをループし、スレッドで join() を呼び出します。
OPの問題には関係ありませんが、ちょうど1つのスレッドとの同期(より正確にはランデブー)に関心がある場合は、 Exchanger を使用できます
私の場合、子スレッドが何かをするまで親スレッドを一時停止する必要がありました。初期化を完了しました。 CountDownLatch もうまく機能します。
この目的で Thread#join メソッドを使用できます。
同様の問題があり、Java 8 parallelStreamを使用することになりました。
requestList.parallelStream().forEach(req -> makeRequest(req));
とてもシンプルで読みやすいです。舞台裏では、デフォルトのJVMのフォーク結合プールを使用しています。つまり、続行する前にすべてのスレッドが終了するまで待機します。私の場合、これはアプリケーション内の唯一のparallelStreamであるため、適切なソリューションでした。同時に複数のparallelStreamを実行している場合は、以下のリンクを読んでください。
並列ストリームに関する詳細情報 こちら 。
これを試して、動作します。
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();
}
}
}
他のスレッドが作業を完了するまで、スレッドメインを待機/ブロックします。
@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();
}
}
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フレームワークを使用した呼び出し可能インターフェイスの使用。
@も参照
Executorサービスを使用して、ステータスや完了などの複数のスレッドを管理できます。 http://programmingexamples.wikidot.com/executorservice を参照してください
いくつかのスレッドが終了するのを待つ小さなヘルパーメソッドを作成しました。
public static void waitForThreadsToFinish(Thread... threads) {
try {
for (Thread thread : threads) {
thread.join();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
既存の答えは、各スレッドを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;
}
}
}
メインスレッドは、グループ内のすべてのスレッドが終了するまで待機します。