Java CyclicBarrierとCountDownLatch

Java CyclicBarrierとCountDownLatch

1. 前書き

このチュートリアルでは、CyclicBarrierCountDownLatchを比較し、2つの類似点と相違点を理解しようとします。

2. これは何?

並行性に関しては、それぞれが達成しようとしていることを概念化することは難しい場合があります。

何よりもまず、両方のCountDownLatch and CyclicBarrier are used for managing multi-threaded applications

そして、they are both intended to express how a given thread or group of threads should wait.

2.1. CountDownLatch

CountDownLatchは、スレッドがwaitsし、他のスレッドがラッチ上でゼロに達するまでcount downする構造です。

これは、準備中のレストランでの料理のようなものと考えることができます。 どの料理人がサイトをいくつ準備しても、ウェイターはすべてのアイテムが皿に載るまでwait秒でなければなりません。 プレートがサイトをとる場合、料理人はプレートに置く各アイテムのラッチをcount down します。

2.2. CyclicBarrier

CyclicBarrier は、すべてのスレッドがarriveになるまで、スレッドのグループwaitsが一緒になる再利用可能な構造です。 その時点で、障壁が破られ、オプションでactionを取得できます。

これは友人のグループのように考えることができます。 彼らはレストランで食事をするたびに、彼らが会うことができる共通のポイントを決定します。 彼らはそこでお互いにwait し、全員がarrivesのときだけ、一緒に食事をするためにレストランに行くことができます。

2.3. 参考文献

また、これらのそれぞれの詳細については、それぞれCountDownLatchCyclicBarrierに関する以前のチュートリアルを参照してください。

3. タスク対 スレッド

これら2つのクラスのセマンティックの違いのいくつかを詳しく見ていきましょう。

定義に記載されているように、CyclicBarrierを使用すると、複数のスレッドが相互に待機できますが、CountDownLatchを使用すると、1つ以上のスレッドが複数のタスクの完了を待機できます。

つまり、CyclicBarrier maintains a count of threadsに対してCountDownLatch maintains a count of tasksです。

次のコードでは、カウントが2のCountDownLatchを定義します。 次に、1つのスレッドからcountDown()を2回呼び出します。

CountDownLatch countDownLatch = new CountDownLatch(2);
Thread t = new Thread(() -> {
    countDownLatch.countDown();
    countDownLatch.countDown();
});
t.start();
countDownLatch.await();

assertEquals(0, countDownLatch.getCount());

ラッチがゼロに達すると、await の呼び出しが返されます。

この場合、we were able to have the same thread decrease the count twice.に注意してください

ただし、CyclicBarrier,はこの点で異なります。

上記の例と同様に、カウントが2のCyclicBarrier,を再度作成し、同じスレッドからawait()を呼び出します。

CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread t = new Thread(() -> {
    try {
        cyclicBarrier.await();
        cyclicBarrier.await();
    } catch (InterruptedException | BrokenBarrierException e) {
        // error handling
    }
});
t.start();

assertEquals(1, cyclicBarrier.getNumberWaiting());
assertFalse(cyclicBarrier.isBroken());

ここでの最初の違いは、待機しているスレッド自体がバリアであることです。

次に、さらに重要なことに、the second await() is uselessです。 A single thread can’t count down a barrier twice.

実際、別のスレッドがawait()を呼び出すにはtwaitである必要があるため、カウントを2にするために–tawait() への2回目の呼び出しは実際には行われませんバリアがすでに破られるまで呼び出されます!

私たちのテストでは、the barrier hasn’t been crossed because we only have one thread waiting and not the two threads that would be required for the barrier to be tripped.これはfalseを返すcyclicBarrier.isBroken()メソッドからも明らかです。

4. 再利用性

これら2つのクラスの2番目に大きな違いは、再利用性です。 詳細については、when the barrier trips in CyclicBarrier, the count resets to its original value.CountDownLatch is different because the count never resets.

与えられたコードでは、カウント7でCountDownLatchを定義し、20回の異なる呼び出しでカウントします。

CountDownLatch countDownLatch = new CountDownLatch(7);
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
    es.execute(() -> {
        long prevValue = countDownLatch.getCount();
        countDownLatch.countDown();
        if (countDownLatch.getCount() != prevValue) {
            outputScraper.add("Count Updated");
        }
    });
}
es.shutdown();

assertTrue(outputScraper.size() <= 7);

20の異なるスレッドがcountDown()を呼び出しても、ゼロに達するとカウントはリセットされないことがわかります。

上記の例と同様に、カウント7のCyclicBarrier を定義し、20の異なるスレッドから待機します。

CyclicBarrier cyclicBarrier = new CyclicBarrier(7);

ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
    es.execute(() -> {
        try {
            if (cyclicBarrier.getNumberWaiting() <= 0) {
                outputScraper.add("Count Updated");
            }
            cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            // error handling
        }
    });
}
es.shutdown();

assertTrue(outputScraper.size() > 7);

この場合、ゼロに達すると、元の値にリセットすることで、新しいスレッドが実行されるたびに値が減少することがわかります。

5. 結論

全体として、CyclicBarrierCountDownLatch はどちらも、複数のスレッド間の同期に役立つツールです。 ただし、提供する機能の点で根本的に異なります。 どちらが仕事に適しているかを判断するときは、それぞれを慎重に検討してください。

いつものように、議論されたすべての例はover on Githubにアクセスできます。