web-dev-qa-db-ja.com

CountDownLatchおよびCyclicBarrierの実際の例

1つの例は、CountDownLatchとCyclicBarrierの違いを説明していたトレーナーの1人が示しています。

CountDownLatch :10人で石を持ち上げることができるため、10人全員が来るのを待つとします。その後、あなただけが石を持ち上げることができます。

CyclicBarrier :ピクニックに行く場合、最初に、すべての人が旅を始める共通の場所で会う必要があります。

誰かがこれらのコメントに同意する場合は、詳細を教えてください。

これらのクラスのSun APIはすでに読みました。しかし、もう少し説明が必要です。

27
Sunny Gupta

主な違いは、CountDownLatchを使用するすべてのスレッドが両方の役割を実行する一方で、CyclicBarrierはスレッドをウェイターと到着者に分離することです。

  • ラッチを使用すると、ウェイターは最後に到着するスレッドが到着するのを待ちますが、それらの到着スレッドは自分自身を待機することはありません。
  • バリアを使用すると、すべてのスレッドが到着し、最後のスレッドが到着するのを待ちます。

ラッチの例では、10人全員が石を持ち上げるのを待つ必要があることを示しています。これはそうではありません。より現実的な例は、各学生がテストを提出するのを辛抱強く待つ試験プロンプターです。学生は、試験を終えて自由に出発できるのを待つ必要はありません。最後の生徒が試験を提出すると(または制限時間が切れると)、プロンプターは待機を停止し、テストを終了します。

37
David Harkness

架空の劇場で:

  • 一人だけが劇を見ることを許可されている場合、それはMutexと呼ばれます。
  • N人がプレイを見ることを許可されている場合、これはSemaphoreと呼ばれます。誰かが劇中に劇場を去った場合、他の人が劇を見ることを許可することができます。
  • すべての人が劇場を出るまで誰も入場を許可されない場合、これはCountDownLatchと呼ばれます。ここに一人一人が劇場を去る自由意志があります。
  • すべての人が劇場に入るまでプレイが開始されない場合、これはCyclicBarrierと呼ばれます。ここでは、ショーマンは、すべての人が席に入るまで、ショーを開始できません。プレイが終了すると、同じバリアが次のショーに適用されます。

ここでは、人はスレッドであり、遊びはリソースです。

42
Shailesh kumar

実際の例私はすべての答えに実際の例が欠けているのがわかります。これらのクラスをソフトウェアレルムで使用する方法と同様

  1. CountDownLatchマルチスレッドのダウンロードマネージャー。ダウンロードマネージャーは、ファイルの各部分を同時にダウンロードするために複数のスレッドを開始します(サーバーがダウンロードする複数のスレッドをサポートしている場合)。ここで、各スレッドはインスタンス化されたラッチのカウントダウンメソッドを呼び出します。すべてのスレッドの実行が完了すると、カウントダウンラッチに関連付けられたスレッドは、異なる部分にあるパーツを1つのファイルに統合します

  2. CyclicBarrier上記と同じシナリオです。ただし、ファイルがP2Pからダウンロードされると仮定します。再び複数のスレッドがピースをダウンロードします。しかし、ここでは、特定の時間間隔の後にダウンロードしたピースの整合性チェックを実行するとします。ここでは循環障壁が重要な役割を果たします。各時間間隔の後、各スレッドはバリアで待機するため、cyclibarrierに関連付けられたスレッドは整合性チェックを実行できます。 CyclicBarrierのおかげで、この整合性チェックは複数回実行できます

何か問題があれば訂正してください。

14
FatherMathew

使用例1大きなジョブを10の小さなタスクに分割し、それぞれを1つのスレッドと仮定します。完了したジョブを検討する前に、そのスレッドからの10個のタスクの終了を待つ必要があります。

したがって、メインのジョブ開始スレッドは、使用されるスレッドの数にCountDownLatchを初期化し、タスクをスレッドに分散し、awaitメソッドでラッチがゼロになるのを待ちます。各エグゼキュータースレッドは、タスクの最後にcountDownを呼び出します。最後に、すべてのジョブが完了したと見なされるように、すべてのスレッドが終了するとメインスレッドがウェイクアップされます。このシナリオでは、CountDownLatch javadocで説明されているdoneSignalラッチを使用します。

使用例2大きなジョブをn個のスレッドに分散されたn * m個のタスクに分割したとします。 mは行列の行に対応し、行ごとに計算する合計があります。その場合、行の合計が計算されるように、各タスクの終了後にスレッドを同期する必要があります。その場合、スレッド数nで初期化されたCyclicBarrierを使用して、各行の計算が終了するまで待機します(実際にはm回)。

両方を比較するために、CountDownLatchは1回だけ使用されることになっており、CyclicBarrierは、アルゴリズムが一連のスレッドの同期ポイントを必要とする回数だけ使用できます。

4
Yves Martin

CyclicBarrierは再利用可能であるため、ツアーの次の区間に進む前に全員がウェイポイントで出会うレースツアーに似ています。

4
trashgod

理論的な違い:

CountDownLatchでは、メインスレッドは他のスレッドが実行を完了するのを待ちます。 CyclicBarrierでは、ワーカースレッドはお互いの実行が完了するのを待ちます。

カウントがゼロに達してラッチが開いたら、同じCountDownLatchインスタンスを再利用することはできません。一方、バリアを解除すると、バリアをリセットしてCyclicBarrierを再利用できます。

実際の例:-

CountDownLatch:マネージャが開発チーム(AとB)間でモジュールを分割し、両方のチームがタスクを完了したときにのみテストするために、QAチームにモジュールを割り当てたいというITの世界シナリオを考えてみましょう。

ここでは、マネージャースレッドがメインスレッドとして機能し、開発チームがワーカースレッドとして機能します。マネージャースレッドは、開発チームのスレッドがタスクを完了するのを待ちます。

CyclicBarrier:マネージャが開発チーム(AとB)の間でモジュールを分割した同じITの世界シナリオを考えてみます。彼は休暇を取り、両方のチームがそれぞれのタスクを完了するのを待つように両方のチームに依頼しました。両方が完了したら、テストのためにQAチームに割り当てます。

ここでは、マネージャースレッドがメインスレッドとして機能し、開発チームがワーカースレッドとして機能します。開発チームのスレッドは、タスクの完了後、他の開発チームのスレッドを待ちます。

3
V Jo

CountDownLatch:すべてのスレッドに実行させる場合

何か+カウントダウン

その他の待機中(カウントがゼロに到達するため)スレッドが続行できるように、カウントダウンラッチを使用できます。実際にカウントダウンを行った以前のすべてのスレッドはこの状況で続行できますが、(latch.countdown()の後に処理される行は、他のスレッドがラッチに達するまで待機した後)であるという保証はありません。countdown()その他の待機中のスレッドは、latch.await()がゼロに達した後にのみ開始することが保証されています。

CyclicBarrier:すべてのスレッドに

何かをする+共通のポイントで待つ+何かをする

(各待機呼び出しは、スレッドがさらに続行するための待機時間を短縮します)

CyclicBarrier機能は、CountDownLatchによって、すべてのスレッドによって、latch.countdown()に続いて、latch.await()を呼び出すことによって1回だけ実行できます。

ただし、カウントダウンラッチをリセット/再利用することはできません。

CyclicBarrierを使用した最良の例は、複数のキャッシュ(複数のスレッドによってウォームアップ)を初期化してから、さらに処理を開始することであり、他のキャッシュをSyncで再度初期化したいと思っていました。

1
Rohit Sachan

これが私の観察です:-----> 1.何を使用するか:循環バリアでは、スレッドは他のスレッドが出力を出すのを待たなければならず、その後すべてのスレッドが処理を再開する必要があります。そのため、実行が完了すると、各スレッドはawait()メソッドを呼び出して待機します。バリアがすべてのスレッドに到達したことを検出すると、バリアは待機中のすべてのスレッドに通知し、さらに実行を再開できます。バリアはカウントを追跡します。

CountDownLatchでは、単一のメインスレッドがすべてのスレッドが完了するまで待機します。各スレッドは、実行が完了するとカウントを1つ減らします。カウントが0に達すると、メインスレッドはさらに実行を再開できます。メインスレッドはカウントを追跡します。

Phaser:どちらの方法でも、スレッドの数は事前にわかっている必要があります。スレッドを動的に追加/削除することはできません。カウントに達しない場合、カウントを追跡しているスレッドは無限に待機します。 Phaserを使用すると、スレッドの数は動的になり、時間とともに変化します。サイクリックバリアに似ています。スレッドはバリアに登録されます。実行が完了すると、2つのオプションがあります。バリアポイントに到着したことを通知し、他の人を待たずにフェイザーから登録を解除できます。 2番目のオプションは、他の登録済みスレッドがバリアポイントに到達するのを待つことができることです。

  1. カウント:CyclicBarrierの作成中に、他のスレッドが完了するのを待つ場合は、ワーカースレッドの数にメインスレッドが含まれます。 CountDownLatchを作成するときは、メインスレッドが完了するまで待機するワーカースレッドの数を指定する必要があります。 CountDownLatchにはメインスレッドとワーカースレッドの概念があり、ラッチの作成中はメインの待機スレッドはカウントに含まれません。 Phaserは、登録されているスレッドの現在の数を返すことができます。

  2. Await()の意図:CyclicBarrier :: await()では、メインスレッドを含むすべてのスレッドが等しく、互いに待機します。したがって、await()はすべてのスレッド(ワーカーとメインスレッド)で指定する必要があります。 CountDownLatch :: await()はメインスレッドでのみ指定され、他のワーカースレッドが0になるまでメインスレッドを待機させます。したがって、両方のawait()の内部実装は異なります。 Phaserには2つの概念があります。他のスレッドのバリアへの到着(arrive()と到着とデレジスタ())と待機(awaitAdvance(phase_number))です。

  3. パーティーと待機スレッド:CountDownLatchは待機スレッドの数を提供できませんが、パーティー(cl.getCount())を提供できます。CyclicBarrierは待機スレッドcb.getNumberWaiting()、およびparty(cb.getParties())を提供できません

  4. 作業の責任:countdownlatchのワーカースレッドはcountdown()を実行する必要があり、await()は単一のメインスレッドによって実行されます。 CyclicBarrierワーカーとメインスレッドでは、すべてがお互いにawait()のみを実行します。

  5. 再利用:CyclicBarrierを再利用できます。 cb.await()は、t1、t2、mainなどの新しいスレッドで機能します。また、新しいスレッドt3、t4およびmainのcb.await()への2番目の呼び出しも機能します。 Mainは両方の呼び出しで待機します。つまり、バリアが終了した後、システムは自動的にカウント(またはreset())を内部的にリセットします。 CountDownLatchは再利用できません。 -cl.await()は、t1、t2、mainなどの新しいスレッドで機能します。メインスレッドはt1、t2が完了するのを待ちます。ただし、2番目のcl.await()の呼び出しでは、新しいスレッドt3、t4が呼び出され、mainは待機しません。セット内のすべてのスレッドがバリアポイントを通過すると、フェイザーオブジェクトを再利用できます。

  6. 終了イベントの後:作成中、終了イベントはCountDownLatchで指定できませんが、CyclicBarrierで指定できます。

クラスMyClass {

static class MyThread implements Runnable
{

    long waitTime;
    CyclicBarrier cyclicBarrier;
    CountDownLatch countdownlatch;
    Phaser phaser;


    MyThread(  long waitTime, CyclicBarrier cyclicBarrier, CountDownLatch countdownlatch, Phaser phaser){
        this.waitTime = waitTime;
        this.cyclicBarrier = cyclicBarrier;
        this.countdownlatch = countdownlatch;
        this.phaser = phaser;
        this.phaser.register(); //Note additional step here
    }

    @Override
    public void run() {

            try {

                Thread.sleep(waitTime);

                // Diff 4 -----> countdownlatch worker threads need to do countdown and await is done by one single main thread
                //, cyclicBarrier worker threads await on each other
                countdownlatch.countDown(); 
                cyclicBarrier.await();
                phaser.arriveAndAwaitAdvance();

                System.out.println("cyclicBarrier :: " + 
                        ", name :: " + Thread.currentThread().getName() 
                        + ", parties :: " + cyclicBarrier.getParties() 
                        + ", waiting :: "+ cyclicBarrier.getNumberWaiting()); 

                System.out.println("countdownlatch :: " + 
                            "name :: " + Thread.currentThread().getName()  +
                         ", parties :: "+countdownlatch.getCount() +
                         ", waiting :: " + "No method!!" ); 
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase()); 

                phaser.arriveAndAwaitAdvance();
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase());

                phaser.arriveAndAwaitAdvance();
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase());
                phaser.arriveAndDeregister(); 
                if (phaser.isTerminated()) { 
                    System.out.println("Phaser is terminated"); 
                } 

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

    }

}

public static class MyCBFinishEvent implements Runnable{

    public void run() {

           System.out.println("All threads have reached common barrier point "
                        + ", CyclicBarrrierFinishEvent has been triggered");
           System.out.println("You can update shared variables if any");
    }

}

public static void main(String [] args) throws InterruptedException, BrokenBarrierException{
    //Diff 1 ----- > No finish event can be given in CountDownLatch
    //Diff 5 ------> CyclicBarrier no of worker threads includes main thread, 
    //CountDownLatch is just how many threads, the main waiting thread is not included in count.
    CyclicBarrier cb = new CyclicBarrier(3, new MyCBFinishEvent());
    CountDownLatch cl = new CountDownLatch(2);
    Phaser ph = new Phaser();

    //Diff 2 ----> CountDownLatch cant give num of waiting threads, CyclicBarrier can getNumberWaiting threads
     System.out.println("Start CyclicBarrier - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
     System.out.println("Start CountDownLatch - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

    Runnable t1 = new Thread(new MyThread( 10000, cb, cl, ph));
    Runnable t2 = new Thread(new MyThread( 5000, cb, cl,ph));
     Thread tt1 = new Thread(t1, "t1");
     Thread tt2 = new Thread(t2, "t2");
     tt1.start();
     tt2.start();

     //Diff 6 ---- > await meaning Main waits for t1 and t2 to complete, 
     //CyclicBarrier all are equal. each thread including main thread, if it wants to wait has to do await. 
     //CountDownLatch concept of waiting and workers. main thread await waits till other worker threads make count to 0.
     cb.await();
     cl.await();

     System.out.println("End CyclicBarrier call 1 - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
     System.out.println("End CountDownLatch call 1 - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

     System.out.println("main start created t3, t4 - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

     Runnable t3 = new Thread(new MyThread( 6000, cb, cl,ph));
        Runnable t4 = new Thread(new MyThread( 100, cb, cl,ph));
         Thread tt3 = new Thread(t3, "t3");

         Thread tt4 = new Thread(t4, "t4");

         tt3.start();
         tt4.start();

        //Diff -3 -----> 
         //CyclicBarrier - can be reused, main thread waited for t3, t4 to complete.
         //CountDownLatch - for first cl.await(), main waited... second cl.await() call main did not wait!!! 
         cb.await();
         cl.await();


         System.out.println("End main - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
         System.out.println("end main parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

}

}

0
BigQ

名前が示すように、循環バリアは循環で使用できます。例:さまざまな求人ポータルフィードからN件の履歴書を探している会社です。優先度順に並べられたスキルを含むスキルセット配列があります。 ex Java、c#、pythonの場合。 Javaスキルセットに一致するN個の履歴書を見つけたいのですが、必要な数の履歴書が見つからない場合は、次のスキルセットなどで再度検索します。

割り当てられたジョブフィードで、履歴書をスキャンするワーカーを作成します。両方の労働者は、ジョブフィードの主要なスキルセット検索から始めます。

検索ワーカーを実行した後、N個の履歴書が見つかったかどうかを確認します。見つかった場合、ワーカーはバリアをリセットして戻ります。そうでない場合は、他のワーカーが完了するのを待ちます。それでもN個の履歴書が見つからなかった場合、スキルセット配列の次のスキルで検索が再開されます。したがって、新しい循環バリアを作成する必要なく、検索を再帰的/循環的に呼び出すことができます。

0
user1849310