私は、Webからいくつかの情報を同時に取得し、バッファクラスの5つの異なるフィールドに入力する5つのスレッドを持つアプリケーションを作成しています。
すべてのスレッドがジョブを終了したら、バッファデータを検証してデータベースに保存する必要があります。
これを行うにはどうすればよいですか(すべてのスレッドが作業を終了したときに警告されます)?
私は、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.
while(!es.awaitTermination(1, TimeUnit.MINUTES));
es.shutdown();
か?私は、私が使用してスレッドを実行するコード記述した場合es.execute(runnableObj_ZipMaking);
にtry
ブロックとでfinally
、私はと呼ばれるがboolean finshed = es.awaitTermination(10, TimeUnit.MINUTES);
。したがって、これは、すべてのスレッドが作業を完了するか、タイムアウトが発生するまで(いずれが先でも)待機する必要があると思います。私の仮定は正しいですか?または呼び出しshutdown()
は必須ですか?
join
スレッドにできます。スレッドが完了するまで、結合はブロックされます。
for (Thread thread : threads) {
thread.join();
}
をjoin
スローすることに注意してくださいInterruptedException
。その場合はどうするかを決める必要があります(たとえば、他のスレッドをキャンセルして、不要な作業が行われないようにします)。
t.join();
は、現在のスレッドがスレッドがt
終了するまでブロックすることを意味します。スレッドには影響しませんt
。
さまざまなソリューションをご覧ください。
join()
APIは、Javaの初期バージョンで導入されました。JDK 1.5リリース以降、この並行パッケージではいくつかの優れた代替手段が利用可能です。
指定されたタスクを実行し、すべてが完了したときにステータスと結果を保持するFutureのリストを返します。
コード例については、この関連するSEの質問を参照してください。
他のスレッドで実行されている一連の操作が完了するまで、1つまたは複数のスレッドを待機させる同期支援。
A たCountDownLatchは、指定されたカウントで初期化されます。awaitメソッドは、
countDown()
メソッドの呼び出しによって現在のカウントがゼロに達するまでブロックします。その後、待機中のすべてのスレッドが解放され、後続のawaitの呼び出しはすぐに戻ります。これはワンショット現象です。カウントをリセットすることはできません。カウントをリセットするバージョンが必要な場合は、CyclicBarrierの使用を検討してください。
の使用法については、この質問を参照してください CountDownLatch
送信後に作成されたすべてのFutureオブジェクトを反復処理しますExecutorService
他の人からのThread.join()
提案とは別に、java 5はexecutorフレームワークを導入しました。そこではThread
オブジェクトを操作しません。代わりに、Callable
またはRunnable
オブジェクトをエグゼキュータに送信します。複数のタスクを実行してその結果を順不同で返すための特別なエグゼキューターがあります。それはExecutorCompletionService
:
ExecutorCompletionService executor;
for (..) {
executor.submit(Executors.callable(yourRunnable));
}
次に、返すオブジェクトtake()
がなくなるまでFuture<?>
、つまり、すべてのオブジェクトが完了するまで繰り返し呼び出すことができます。
シナリオによっては、関連する可能性のあるもう1つの点はCyclicBarrier
です。
一連のスレッドがすべてお互いが共通のバリアポイントに到達するのを待機できるようにする同期補助。CyclicBarriersは、時々互いに待機しなければならない固定サイズのスレッドのパーティが関与するプログラムで役立ちます。バリアは、待機中のスレッドが解放された後に再利用できるため、循環型と呼ばれます。
executor.submit
を返しますFuture<?>
。これらのフューチャーをリストに追加してから、get
各フューチャーを呼び出すリストをループ処理します。
Executors
たとえばExecutors.newCachedThreadPool
(または類似の)を使用してコンストラクタをインスタンス化できます
もう1つの可能性はCountDownLatch
オブジェクトです。これは単純な状況で役立ちます。スレッドの数は事前にわかっているため、関連するカウントで初期化し、オブジェクトの参照を各スレッドに渡します。
タスクが完了すると、各スレッドはCountDownLatch.countDown()
内部カウンターをデクリメントして呼び出します。メインスレッドは、他のすべてを開始した後、CountDownLatch.await()
ブロッキング呼び出しを実行する必要があります。内部カウンタが0になるとすぐに解放されます。
このオブジェクトを使用すると、InterruptedException
もスローできることに注意してください。
他のスレッドが作業を完了するまで、スレッドメインを待機/ブロックします。
ASは@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.
}
ThreadedクラスのサンプルコードLatchTask
。アプローチをテストするには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クラスを参照してください。実行フレームワーク:ExecutorServiceを使用してスレッドプールを作成し、Futureを使用して非同期タスクの進行状況を追跡できます。
submit(Runnable)
、submit(Callable)
Future Objectを返します。future.get()
関数を使用して、作業スレッドが作業を完了するまでメインスレッドをブロックできます。
invokeAll(...)
-各Callableの実行結果を取得できるFutureオブジェクトのリストを返します。
エグゼキューターフレームワークでインターフェースRunnable、Callableを使用する例を見つけます。
@も参照
この目的でThreadf#joinメソッドを使用できます。
OPの問題には関係ありませんが、1つのスレッドとの同期(より正確には、ランデブー)に関心がある場合は、Exchangerを使用できます。
私の場合、子スレッドが何かを行うまで、たとえば初期化が完了するまで、親スレッドを一時停止する必要がありました。たCountDownLatchはまたよく働きます。
executorサービスを使用して、ステータスや完了などの複数のスレッドを管理できます。http://programmingexamples.wikidot.com/executorserviceを参照してください
これを試して、動作します。
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();
}
}
}
同様の問題があり、Java 8 parallelStreamを使用してしまいました。
requestList.parallelStream().forEach(req -> makeRequest(req));
とてもシンプルで読みやすいです。背後では、デフォルトのJVMのフォーク結合プールを使用しています。つまり、すべてのスレッドが完了するのを待ってから続行します。私の場合、それが私のアプリケーションで唯一のparallelStreamだったので、それはきちんとしたソリューションでした。複数のparallelStreamを同時に実行している場合は、以下のリンクをお読みください。
並列ストリームの詳細については、こちらをご覧ください。
既存の答えは、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;
}
}
}
メインスレッドは、グループ内のすべてのスレッドが完了するのを待ちます。
いくつかのスレッドが完了するのを待つ小さなヘルパーメソッドを作成しました。
public static void waitForThreadsToFinish(Thread... threads) {
try {
for (Thread thread : threads) {
thread.join();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
これをメインスレッドで使用します:while(!executor.isTerminated()); executorサービスからすべてのスレッドを開始した後、このコード行を配置します。これは、エグゼキューターによって開始されたすべてのスレッドが終了した後でのみメインスレッドを開始します。必ずexecutor.shutdown();を呼び出してください。上記のループの前。