Java.util.concurrent API を読んでいたところ、
CountDownLatch
:他のスレッドで実行されている一連の操作が完了するまで1つ以上のスレッドを待機させる同期支援。CyclicBarrier
:スレッドのセットがすべて共通のバリアポイントに到達するまで相互に待機できるようにする同期支援。私には両方とも平等に思えますが、もっともっとあるはずです。
たとえば、CoundownLatch, the countdown value could not be reset, that can happen in the case of CyclicBarrier
など。
2つの間に他の違いはありますか?
カウントダウンの値をリセットしたいuse cases
とは何ですか?
大きな違いの1つは、 CyclicBarrier が(オプションの)実行可能なタスクを取り、共通のバリア条件が満たされると実行されることです。
また、バリアで待機しているクライアントの数と、バリアをトリガーするのに必要な数を取得できます。トリガーされると、バリアはリセットされ、再び使用できます。
単純なユースケース-サービスの起動など... CountdownLatchで問題ありません。 CyclicBarrierは、より複雑な調整タスクに役立ちます。そのようなことの例は、並列計算です-複数のサブタスクが計算に関与している場合- MapReduce のようなものです。
別の違いがあります。
CyclicBarrier
を使用する場合、バリアをトリガーする待機スレッドの数を指定することを前提としています。 5を指定する場合、await()
を呼び出すには少なくとも5つのスレッドが必要です。
CountDownLatch
を使用する場合、countDown()
の呼び出し回数を指定すると、すべての待機スレッドが解放されます。つまり、CountDownLatch
は単一のスレッドでのみ使用できます。
「どうしてそんなことをするの?」とあなたは言うかもしれません。コールバックを実行する他の誰かによってコーディングされた神秘的なAPIを使用していると想像してください。特定のコールバックが何度も呼び出されるまで、スレッドの1つを待機させる必要があります。コールバックが呼び出されるスレッドがわかりません。この場合、CountDownLatch
は完璧ですが、CyclicBarrier
を使用してこれを実装する方法は考えられません(実際にはできますが、タイムアウトが発生します...うーん!)。
CountDownLatch
がリセットされることを望みます!
誰もまだ言及していない点の1つは、CyclicBarrier
で、スレッドに問題(タイムアウト、割り込み...)がある場合、await()
に達した他のすべての例外を取得することです。 Javadocを参照してください:
CyclicBarrierは、失敗した同期の試行にすべてまたはなしの破損モデルを使用します:割り込み、失敗、またはタイムアウトのためにスレッドがバリアポイントを途中で離れると、そのバリアポイントで待機している他のすべてのスレッドもBrokenBarrierException(またはInterruptedExceptionによって異常のままになります)彼らもほぼ同時に中断された場合)。
JavaDocは違いを明示的に説明したと思います。ほとんどの人はCountDownLatchをリセットできないことを知っていますが、CyclicBarrierはリセットできます。しかし、これが唯一の違いではなく、CyclicBarrierの名前をResetbleCountDownLatchに変更することもできます。 JavaDocで説明されている目標の観点から違いを伝える必要があります。
CountDownLatch:他のスレッドで実行されている一連の操作が完了するまで1つ以上のスレッドが待機できるようにする同期エイド。
CyclicBarrier:一連のスレッドが相互に共通のバリアポイントに到達するのをすべて待機できるようにする同期エイド。
CountDownLatchには、1つ以上のスレッドがあり、一連の他のスレッドの完了を待機しています。この状況では、2つのタイプのスレッドがあります。1つのタイプは待機中、もう1つのタイプはタスクを終了した後、待機中または終了した可能性があります。
CyclicBarrierでは、スレッドのタイプは1つだけで、お互いを待っていますが、同等です。
主な違いは、CountdownLatchのJavadocに文書化されています。すなわち:
CountDownLatchは、指定されたカウントで初期化されます。 awaitメソッドは、countDown()メソッドの呼び出しにより現在のカウントがゼロに達するまでブロックします。その後、待機中のすべてのスレッドが解放され、その後のawaitの呼び出しが直ちに返されます。これはワンショット現象です。カウントはリセットできません。カウントをリセットするバージョンが必要な場合は、CyclicBarrierの使用を検討してください。
ソース 1.6 Javadoc
この質問はすでに十分に回答されていますが、コードを投稿することで少し価値を高めることができると思います。
循環バリアの動作を説明するために、サンプルコードを作成しました。バリアが傾けられるとすぐに、自動的にリセットされ、再び使用できるようになります(したがって、「循環」です)。プログラムを実行するとき、バリアが傾けられた後にのみ「遊びましょう」というプリントアウトがトリガーされることに注意してください。
import Java.util.concurrent.BrokenBarrierException;
import Java.util.concurrent.CyclicBarrier;
public class CyclicBarrierCycles {
static CyclicBarrier barrier;
public static void main(String[] args) throws InterruptedException {
barrier = new CyclicBarrier(3);
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
System.out.println("Barrier automatically resets.");
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
new Worker().start();
}
}
class Worker extends Thread {
@Override
public void run() {
try {
CyclicBarrierCycles.barrier.await();
System.out.println("Let's play.");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
CountDownLatchは、1回限りの同期に使用されます。 CountDownLatchを使用している間、どのスレッドでもcountDown()を何度でも呼び出すことができます。 await()を呼び出したスレッドは、他のブロックされていないスレッドによるcountDown()の呼び出しにより、カウントがゼロになるまでブロックされます。 CountDownLatchのjavadoc 状態:
Awaitメソッドは、countDown()メソッドの呼び出しにより現在のカウントがゼロに達するまでブロックします。その後、すべての待機スレッドが解放され、その後のawaitの呼び出しはすぐに戻ります。 ...
別の典型的な使用法は、問題をN個の部分に分割し、各部分をその部分を実行してラッチでカウントダウンするRunnableで記述し、すべてのRunnableをエグゼキューターにキューイングすることです。すべてのサブパートが完了すると、調整スレッドは待機をパススルーできます。 (スレッドがこの方法で繰り返しカウントダウンする必要がある場合は、代わりにCyclicBarrierを使用します。)
対照的に、サイクリックバリアは複数の同期ポイントに使用されます。一連のスレッドがループ/フェーズ計算を実行しており、次の反復/フェーズを開始する前に同期する必要がある場合。 CyclicBarrierのjavadoc に従って:
バリアは、待機中のスレッドが解放された後に再利用できるため、サイクリックと呼ばれます。
CountDownLatchとは異なり、await()の各呼び出しはあるフェーズに属し、そのフェーズに属するすべてのパーティがawait()を呼び出すまでスレッドをブロックする可能性があります。 CyclicBarrierによってサポートされる明示的なcountDown()操作はありません。
ラッチと循環障壁について勉強していたときに、この比metaを思いつきました。 cyclicbarriers:会社に会議室があると想像してください。ミーティングを開始するには、一定数のミーティング参加者がミーティングに参加する必要があります(公式にするため)。以下は、通常の会議出席者(従業員)のコードです
class MeetingAtendee implements Runnable {
CyclicBarrier myMeetingQuorumBarrier;
public MeetingAtendee(CyclicBarrier myMileStoneBarrier) {
this.myMeetingQuorumBarrier = myMileStoneBarrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " i joined the meeting ...");
myMeetingQuorumBarrier.await();
System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
System.out.println("Meeting canceled! every body dance <by chic band!>");
}
}
}
従業員は会議に参加し、他の人が会議を開始するのを待ちます。また、会議がキャンセルされた場合、彼は終了します:)そして、他の人が現れるのを待つのが好きではないボスがあり、患者を失うと、会議をキャンセルします。
class MeetingAtendeeTheBoss implements Runnable {
CyclicBarrier myMeetingQuorumBarrier;
public MeetingAtendeeTheBoss(CyclicBarrier myMileStoneBarrier) {
this.myMeetingQuorumBarrier = myMileStoneBarrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "I am THE BOSS - i joined the meeting ...");
//boss dose not like to wait too much!! he/she waits for 2 seconds and we END the meeting
myMeetingQuorumBarrier.await(1,TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
System.out.println("what WHO canceled The meeting");
} catch (TimeoutException e) {
System.out.println("These employees waste my time!!");
}
}
}
通常、従業員は他の人が現れるのを待ち合わせて会議に来ます。一部の出席者が来ない場合、彼らは無期限に待たなければなりません!いくつかの特別な会議では上司が来て、彼は待つのが好きではありません。
CyclicBarrier meetingAtendeeQuorum = new CyclicBarrier(5);
Thread atendeeThread = new Thread(new MeetingAtendee(meetingAtendeeQuorum));
Thread atendeeThreadBoss = new Thread(new MeetingAtendeeTheBoss(meetingAtendeeQuorum));
atendeeThread.start();
atendeeThreadBoss.start();
出力:
//Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// These employees waste my time!!
// Meeting canceled! every body dance <by chic band!>
別の部外者スレッド(地震)が会議をキャンセルする別のシナリオがあります(リセットメソッドの呼び出し)。この場合、待機中のすべてのスレッドが例外によって起動されます。
class NaturalDisasters implements Runnable {
CyclicBarrier someStupidMeetingAtendeeQuorum;
public NaturalDisasters(CyclicBarrier someStupidMeetingAtendeeQuorum) {
this.someStupidMeetingAtendeeQuorum = someStupidMeetingAtendeeQuorum;
}
void earthQuakeHappening(){
System.out.println("earth quaking.....");
someStupidMeetingAtendeeQuorum.reset();
}
@Override
public void run() {
earthQuakeHappening();
}
}
コードを実行すると、面白い出力が得られます:
// Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// earth quaking.....
// what WHO canceled The meeting
// Meeting canceled! every body dance <by chic band!>
会議室に秘書を追加することもできます。会議が開催された場合、彼女はすべてを文書化しますが、会議の一部ではありません。
class MeetingSecretary implements Runnable {
@Override
public void run() {
System.out.println("preparing meeting documents");
System.out.println("taking notes ...");
}
}
ラッチ:怒っている上司が企業顧客向けの展示会を開催する場合、すべてのものを準備する必要があります(リソース)。すべてのワーカー(スレッド)がジョブを処理するTo Doリストを提供し、To Doリストを確認します(一部のワーカーはペイントを行い、他のワーカーはサウンドシステムを準備します...)。予定リストのすべてのアイテムが完成したら(リソースが提供されたら)、顧客への扉を開くことができます。
public class Visitor implements Runnable{
CountDownLatch exhibitonDoorlatch = null;
public Visitor (CountDownLatch latch) {
exhibitonDoorlatch = latch;
}
public void run() {
try {
exhibitonDoorlatch .await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("customer visiting exebition");
}
}
そして、労働者はどのように展示会を準備していますか:
class Worker implements Runnable {
CountDownLatch myTodoItem = null;
public Worker(CountDownLatch latch) {
this.myTodoItem = latch;
}
public void run() {
System.out.println("doing my part of job ...");
System.out.println("My work is done! remove it from todo list");
myTodoItem.countDown();
}
}
CountDownLatch preperationTodoList = new CountDownLatch(3);
// exhibition preparation workers
Worker electricalWorker = new Worker(preperationTodoList);
Worker paintingWorker = new Worker(preperationTodoList);
// Exhibition Visitors
ExhibitionVisitor exhibitionVisitorA = new ExhibitionVisitor(preperationTodoList);
ExhibitionVisitor exhibitionVisitorB = new ExhibitionVisitor(preperationTodoList);
ExhibitionVisitor exhibitionVisitorC = new ExhibitionVisitor(preperationTodoList);
new Thread(electricalWorker).start();
new Thread(paintingWorker).start();
new Thread(exhibitionVisitorA).start();
new Thread(exhibitionVisitorB).start();
new Thread(exhibitionVisitorC).start();
一言で言えば、キーを理解するため機能 2つの違い:
public class CountDownLatch {
private Object mutex = new Object();
private int count;
public CountDownLatch(int count) {
this.count = count;
}
public void await() throws InterruptedException {
synchronized (mutex) {
while (count > 0) {
mutex.wait();
}
}
}
public void countDown() {
synchronized (mutex) {
if (--count == 0)
mutex.notifyAll();
}
}
}
そして
public class CyclicBarrier {
private Object mutex = new Object();
private int count;
public CyclicBarrier(int count) {
this.count = count;
}
public void await() throws InterruptedException {
synchronized (mutex) {
count--;
while(count > 0)
mutex.wait();
mutex.notifyAll();
}
}
}
もちろん、ノンブロッキング、時間待ち、診断、および上記の回答で詳細に説明されたすべてのような機能を除きます。
ただし、上記のクラスは、提供された機能内で、対応する名前と完全に機能し、同等です。
別の注意として、CountDownLatch
の内部クラスはAQS
をサブクラス化しますが、CyclicBarrier
はReentrantLock
を使用します(別の方法であるか、両方がAQSを使用するか、両方がLockを使用する可能性があります-パフォーマンス効率を損なうことなく)
CyclicBarrierの場合、すべての子スレッドがbarrier.await()の呼び出しを開始するとすぐに、RunnableがBarrierで実行されます。各子スレッドのbarrier.awaitは、完了するまでに異なる時間を要し、それらはすべて同時に終了します。
明らかな違いの1つは、NのCyclicBarrierが1サイクルでリリースされるのをNスレッドのみ待機できることです。ただし、CountDownLatchのNで待機できるスレッドの数に制限はありません。カウントダウンの減少は、1つのスレッドによってN回、またはNスレッドごとに1回または組み合わせて実行できます。
CountDownLatchでは、メインスレッドは他のスレッドの実行が完了するまで待機します。 CyclicBarrierでは、ワーカースレッドは互いの実行が完了するまで待機します。
同じものを再利用することはできませんCountDownLatchカウントがゼロに達してラッチがオープンすると、インスタンスCyclicBarrierはバリアをリセットして再利用できます。
CountDownLatchは、あらゆるもののカウントダウンです。 CyclicBarrierはスレッドのみのカウントダウンです
5つのワーカースレッドと1つの荷送人スレッドがあり、労働者が100個のアイテムを生産すると、荷送人がそれらを出荷すると仮定します。
CountDownLatchの場合、カウンターはワーカーまたはアイテムに配置できます
CyclicBarrierの場合、カウンターはワーカーに対してのみ可能です
ワーカーがアイテムのCountDownLatchを使用して無限にスリープ状態になった場合、配送業者は出荷できます。ただし、CyclicBarrierでは、配送業者を呼び出すことはできません
@Kevin Leeと@JonオプションのRunnableでCyclicBarrierを試しました。 CyclicBarrierが開始された後に実行されたように見えます。コードと出力は次のとおりです
静的CyclicBarrierバリア。
public static void main(String[] args) throws InterruptedException {
barrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("I run in the beginning and after the CyclicBarrier is tipped");
}
});
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
System.out.println("Barrier automatically resets.");
new Worker().start();
Thread.sleep(1000);
new Worker().start();
Thread.sleep(1000);
new Worker().start();
}
出力
I run in the beginning and after the CyclicBarrier is tipped
Let's play.
Let's play.
Let's play.
Barrier automatically resets.
I run in the beginning and after the CyclicBarrier is tipped
Let's play.
Let's play.
Let's play.