Народный класс инструментов параллелизма Java — CountDownLatch, CyclicBarrier

Java задняя часть
Народный класс инструментов параллелизма Java — CountDownLatch, CyclicBarrier

Оригинальная статья и краткое изложение опыта и жизненные перипетии от набора в школу до фабрики А

Нажмите, чтобы узнать подробностиwww.codercc.com

1. Таймер обратного отсчета CountDownLatch

Когда многопоточное сотрудничество выполняет бизнес-функции, иногда необходимо дождаться выполнения задач другими потоками, прежде чем основной поток сможет продолжить выполнение бизнес-функций.В таких бизнес-сценариях обычно можно использовать метод соединения класса Thread, чтобы позволить основной поток После того, как поток ожидает завершения выполнения объединенного потока, основной поток может продолжить выполнение. Конечно, это также можно сделать с помощью механизма обмена сообщениями между потоками. Фактически, класс инструментов параллелизма Java предоставляет нам такой класс инструментов, как «обратный отсчет», который может быть очень удобным для выполнения этого бизнес-сценария.

Чтобы понять CountDownLatch, давайте возьмем очень распространенный пример. Когда спортсмен бежит в забеге, предполагая, что в забеге участвуют 6 спортсменов, рефери будет измерять время для 6 спортсменов отдельно на финише. Вполне возможно, что когда спортсмен достигает финиша, у судьи на одно задание меньше. Задача судьи не считается выполненной, пока все спортсмены не дойдут до финиша. Этих 6 спортсменов можно сравнить с 6 потоками.Когда поток вызывает метод CountDownLatch.countDown, значение счетчика уменьшается на 1. Когда значение счетчика равно 0, рефери (поток, вызывающий метод await) может продолжать снижаться.

Давайте рассмотрим некоторые важные методы CountDownLatch.

Начнем с конструктора CountDownLatch:

public CountDownLatch(int count)

Конструктор передаст целое число N, а затем вызовет функцию CountDownLatch.countDownМетод уменьшит N на 1. Когда N уменьшится до 0, текущий вызовawaitПоток метода продолжает выполнение.

Методов CountDownLatch не так много, перечислим их один за другим:

  1. await() выдает InterruptedException: поток, вызывающий этот метод, может продолжать выполняться до тех пор, пока N, переданный конструктором, не уменьшится до 0;
  2. await(long timeout, TimeUnit unit): имеет ту же функцию, что и описанный выше метод await, за исключением ограничения по времени. он будет продолжать выполняться. ;
  3. countDown(): уменьшить начальное значение N CountDownLatch на 1;
  4. long getCount(): получить значение, поддерживаемое текущим CountDownLatch;

В следующем примере используется конкретный пример, иллюстрирующий конкретное использование CountDownLatch:

public class CountDownLatchDemo {
private static CountDownLatch startSignal = new CountDownLatch(1);
//用来表示裁判员需要维护的是6个运动员
private static CountDownLatch endSignal = new CountDownLatch(6);

public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(6); for (int i = 0; i < 6; i++) { executorService.execute(() -> { try { System.out.println(Thread.currentThread().getName() + " 运动员等待裁判员响哨!!!"); startSignal.await(); System.out.println(Thread.currentThread().getName() + "正在全力冲刺"); endSignal.countDown(); System.out.println(Thread.currentThread().getName() + " 到达终点"); } catch (InterruptedException e) { e.printStackTrace(); } }); } System.out.println("裁判员发号施令啦!!!"); startSignal.countDown(); endSignal.await(); System.out.println("所有运动员到达终点,比赛结束!"); executorService.shutdown(); } } 输出结果:

pool-1-thread-2 运动员等待裁判员响哨!!! pool-1-thread-3 运动员等待裁判员响哨!!! pool-1-thread-1 运动员等待裁判员响哨!!! pool-1-thread-4 运动员等待裁判员响哨!!! pool-1-thread-5 运动员等待裁判员响哨!!! pool-1-thread-6 运动员等待裁判员响哨!!! 裁判员发号施令啦!!! pool-1-thread-2正在全力冲刺 pool-1-thread-2 到达终点 pool-1-thread-3正在全力冲刺 pool-1-thread-3 到达终点 pool-1-thread-1正在全力冲刺 pool-1-thread-1 到达终点 pool-1-thread-4正在全力冲刺 pool-1-thread-4 到达终点 pool-1-thread-5正在全力冲刺 pool-1-thread-5 到达终点 pool-1-thread-6正在全力冲刺 pool-1-thread-6 到达终点 所有运动员到达终点,比赛结束!

В примере кода установлены два CountDownLatch, первыйendSignalИспользуется для управления тем, что основной поток (арбитр) должен ждать, пока другие потоки (спортсмены) не позволят значению N, поддерживаемому CountDownLatch, уменьшиться до 0. ДругаяstartSignalИспользуется, чтобы позволить основному потоку «отдавать приказы» другим потокам, начальное значение CountDownLatch, на которое ссылается startSignal, равно 1, и метод запуска, выполняемый другими потоками, сначала проходитstartSignal.await()Пусть эти потоки будут заблокированы, пока основной поток не вызоветstartSignal.countDown();, уменьшите значение N на 1, после того как значение N, поддерживаемое CountDownLatch, станет равным 0, другие потоки могут выполняться вниз, а метод запуска, выполняемый каждым потоком, будет передаватьendSignal.countDown();правильноendSignalЗначение обслуживания уменьшается на 1. Поскольку 6 задач отправляются в пул потоков, оно будет уменьшено в 6 раз, поэтомуendSignalПоддерживаемое значение в конечном итоге становится равным 0, поэтому основной потокlatch.await();После того, как блокировка закончилась, выполнение может быть продолжено.

Кроме того, следует отметить, что при вызове метода countDown класса CountDownLatch текущий поток не будет заблокирован и продолжит выполнение, например, в этом примере он продолжит выводитьpool-1-thread-4 到达终点.

2. Циклический барьер: циклический барьер

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

Чтобы понять CyclicBarrier, вот популярный пример. Когда будет спортивная встреча, будет и такой вид спорта, как бег.Смоделируем ситуацию, когда спортсмены заходят на стадион. Предположим, имеется 6 беговых дорожек.В начале игры требуется, чтобы 6 спортсменов стояли на старте точка в начале игры.Бег не начинается до тех пор, пока судья не даст свисток. Начальная точка дорожки эквивалентна «барьеру», который является критической точкой, а эти 6 спортсменов аналогичны нитям, то есть эти 6 нитей должны дойти до указанной точки, а значит, волна может быть собрана до продолжая выполняться, в противном случае каждый поток должен блокироваться и ждать, пока не появится волна. Cyclic означает цикл, то есть CyclicBarrier все еще действует после того, как несколько потоков составляют одну волну, и может продолжать создавать следующую волну. Схема выполнения CyclicBarrier выглядит следующим образом:

CyclicBarrier执行示意图.jpg
Диаграмма выполнения CyclicBarrier.jpg

Когда несколько потоков достигли указанной точки, они могут продолжить выполнение. Это немного похоже на сообщение числа.Предполагая, что 6 нитей эквивалентны 6 спортсменам, число будет сообщено для статистики, когда оно достигнет начальной точки дорожки.Если это окажется 6, волна будет завершено до того, как оно может быть выполнено. **CyclicBarrier по-прежнему действителен после однократного использования и может продолжать использоваться в качестве счетчика, что является одним из отличий от CountDownLatch. ** Здесь 6 потоков, то есть начальное значение счетчика 6, передаются через конструктор CyclicBarrier.

Давайте рассмотрим основные методы CyclicBarrier:

//等到所有的线程都到达指定的临界点
await() throws InterruptedException, BrokenBarrierException

//与上面的await方法功能基本一致,只不过这里有超时限制,阻塞等待直至到达超时时间为止 await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

//获取当前有多少个线程阻塞等待在临界点上 int getNumberWaiting()

//用于查询阻塞等待的线程是否被中断 boolean isBroken()

//将屏障重置为初始状态。如果当前有线程正在临界点等待的话,将抛出BrokenBarrierException。 void reset()

Следует также отметить, что CyclicBarrier предоставляет такой конструктор:

public CyclicBarrier(int parties, Runnable barrierAction)

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

один пример

Давайте рассмотрим использование CyclicBarrier на простом примере. Давайте смоделируем приведенный выше пример спортсмена.

public class CyclicBarrierDemo {
    //指定必须有6个运动员到达才行
    private static CyclicBarrier barrier = new CyclicBarrier(6, () -> {
        System.out.println("所有运动员入场,裁判员一声令下!!!!!");
    });
    public static void main(String[] args) {
        System.out.println("运动员准备进场,全场欢呼............");
    ExecutorService service = Executors.newFixedThreadPool(6);
    for (int i = 0; i &lt; 6; i++) {
        service.execute(() -&gt; {
            try {
                System.out.println(Thread.currentThread().getName() + " 运动员,进场");
                barrier.await();
                System.out.println(Thread.currentThread().getName() + "  运动员出发");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        });
    }
}

}

скопировать код

输出结果: 运动员准备进场,全场欢呼............ pool-1-thread-2 运动员,进场 pool-1-thread-1 运动员,进场 pool-1-thread-3 运动员,进场 pool-1-thread-4 运动员,进场 pool-1-thread-5 运动员,进场 pool-1-thread-6 运动员,进场 所有运动员入场,裁判员一声令下!!!!! pool-1-thread-6 运动员出发 pool-1-thread-1 运动员出发 pool-1-thread-5 运动员出发 pool-1-thread-4 运动员出发 pool-1-thread-3 运动员出发 pool-1-thread-2 运动员出发

Из вывода видно, что когда все 6 спортсменов (потоков) достигли заданной критической точки (барьера), они могут продолжить выполнение, иначе они заблокируются и будут ждать вызоваawait()где

3. Сравнение CountDownLatch и CyclicBarrier

CountDownLatch и CyclicBarrier — это классы инструментов, используемые для управления параллелизмом, и оба они могут рассматриваться как поддерживающие счетчик, но они по-прежнему имеют разные цели:

  1. CountDownLatch обычно используется для того, чтобы поток A ждал, пока несколько других потоков выполнят задачи, прежде чем он выполнится; в то время как CyclicBarrier обычно используется для группы потоков, ожидающих друг друга до определенного состояния, а затем эта группа потоков выполняется в в то же время; CountDownLatch выделяет поток Дождитесь завершения нескольких потоков. CyclicBarrier состоит в том, что несколько потоков ждут друг друга, и после того, как все закончат работу, они будут работать вместе.
  2. После вызова метода countDown CountDownLatch текущий поток не будет блокироваться и будет продолжать выполняться вниз; при вызове метода await CyclicBarrier текущий поток будет блокироваться до тех пор, пока все потоки, указанные CyclicBarrier, не достигнут указанной точки, могут продолжать идти вниз орудие;
  3. CountDownLatch имеет меньше методов и более простые операции, в то время как CyclicBarrier предоставляет больше методов, таких какgetNumberWaiting(), isBroken() для получения текущего состояния нескольких потоков,И конструктор CyclicBarrier может перейти в барьерAction, определяя бизнес-функцию, которая будет выполняться при получении всех потоков;
  4. CountDownLatch нельзя использовать повторно, а CyclicBarrier можно использовать повторно.