Рубрики
Без рубрики

Java CyclicBarrier против обратного отсчета

Сравните и сравните утилиты синхронизации Java CyclicBarrier и CountDownLatch

Автор оригинала: baeldung.

1. введение

В этом уроке мы сравним CyclicBarrier и CountDownLatch и попытаемся понять сходства и различия между ними.

2. Что Это Такое?

Когда дело доходит до параллелизма, может быть сложно концептуализировать то, что каждый из них предназначен для достижения.

Прежде всего, оба CountDownLatch и CyclicBarrier используются для управления многопоточными приложениями .

И, они оба предназначены для выражения того, как данный поток или группа потоков должны ждать.

2.1. Обратный отсчет

A CountDownLatch – это конструкция, в которой поток ждет , в то время как другие потоки обратный отсчет на защелке, пока он не достигнет нуля.

Мы можем думать об этом как о готовящемся блюде в ресторане. Независимо от того, какой повар готовит, сколько бы предметов n ни было, официант должен подождать , пока все предметы не окажутся на тарелке. Если тарелка принимает n предметов, любой повар будет отсчитывать на защелке для каждого предмета, который она кладет на тарелку.

2.2. Циклобарьер

/| CyclicBarrier – это многоразовая конструкция, в которой группа потоков ожидает вместе, пока все потоки не прибудут . В этот момент барьер сломан, и при необходимости может быть предпринято действие .

Мы можем думать об этом как о группе друзей. Каждый раз, когда они планируют поесть в ресторане, они решают общую точку, где они могут встретиться. Они ждут друг друга там, и только когда все прибудут они смогут пойти в ресторан, чтобы поесть вместе.

2.3. Дальнейшее Чтение

И для получения более подробной информации о каждом из них в отдельности обратитесь к нашим предыдущим учебным пособиям по CountDownLatch и CyclicBarrier соответственно.

3. Задачи против Нити

Давайте глубже погрузимся в некоторые семантические различия между этими двумя классами.

Как указано в определениях, CyclicBarrier позволяет нескольким потокам ждать друг друга, в то время как CountDownLatch позволяет одному или нескольким потокам ждать завершения ряда задач.

Короче говоря, CyclicBarrier поддерживает количество потоков , тогда как CountDownLatch поддерживает количество задач .

В следующем коде мы определяем CountDownLatch со счетом два. Затем мы вызываем Обратный отсчет() дважды из одного потока:

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

assertEquals(0, countDownLatch.getCount());

Как только защелка достигает нуля, возвращается вызов await .

Обратите внимание, что в этом случае мы смогли заставить один и тот же поток уменьшить количество в два раза.

Циклобарьер, хотя в этом вопросе все по-другому.

Аналогично приведенному выше примеру, мы создаем 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());

Первое отличие здесь заключается в том, что потоки, которые ждут, сами являются барьером.

Во-вторых, и что более важно, второй await() бесполезен . Один поток не может отсчитывать барьер дважды.

Действительно, потому что t должен ждать вызова другого потока await() – чтобы довести количество до двух – t второй вызов await() фактически не будет вызван, пока барьер уже не будет преодолен!

В нашем тесте барьер не был преодолен, потому что у нас есть только один поток, ожидающий, а не два потока, которые потребуются для срабатывания барьера. Это также очевидно из cyclicBarrier.is Метод Broken () , который возвращает false .

4. Возможность Повторного Использования

Второе наиболее очевидное различие между этими двумя классами-это возможность повторного использования. Чтобы уточнить, когда барьер срабатывает в CyclicBarrier , счетчик сбрасывается до своего первоначального значения. CountDownLatch отличается тем, что счетчик никогда не сбрасывается.

В данном коде мы определяем CountDownLatch со счетом 7 и подсчитываем его через 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 () , счетчик не сбрасывается, как только он достигает нуля.

Как и в приведенном выше примере, мы определяем CyclicBarrier со счетом 7 и ждем его из 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. Заключение

В целом, CyclicBarrier и CountDownLatch являются полезными инструментами для синхронизации между несколькими потоками. Однако они принципиально отличаются с точки зрения функциональности, которую они предоставляют. Тщательно рассмотрите каждый из них, определяя, какой из них подходит для этой работы.

Как обычно, все обсуждаемые примеры доступны на Github .