В этой статье рассмотрим CountDownLatch. В отличие от CyclicBarrier, где все потоки равноправны и ожидают друг друга, с CountDownLatch потоки делятся на два типа — одни уменьшают значение CountDownLatch, а другие ждут, когда он станет равен 0, чтобы продолжиться.
Рассмотрим пример. Допустим студенты выполняют тест и уходят домой (10 человек — 10 потоков), а учитель (1 поток) ждет, когда все сдадут тест, и потом продолжает работу.
Поток студента:
public class TestAndGo implements Runnable { private CountDownLatch countDownLatch; public TestAndGo(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { System.out.println("в процессе тестирования"); countDownLatch.countDown(); System.out.println("пошел домой"); } }
Как видите, тут фигурирует некий CountDownLatch, но на ход потока TestAndGo он не влияет никак — разве что у потока появляется маленькая дополнительная работа — команда
countDownLatch.countDown();
Она уменьшает значение счетчика на 1.
Сам же счетчик используется в другом потоке — потоке учителя (у нас это основной поток — метод main).
В нем мы запускаем 10 потоков TestAndGo и ждем, пока все студенты сдадут тест:
public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch=new CountDownLatch(10); IntStream.range(0,10).forEach((i)-> new Thread(new TestAndGo(countDownLatch)).start()); countDownLatch.await(); System.out.println("все сдали, зачет окончен"); }
Ожидание выражается командой:
countDownLatch.await();
Главный поток останавливается в этом месте и и ждет до тех пор, пока countDownLatch не станет равным 0. Предварительно его создали со значением 10:
CountDownLatch countDownLatch=new CountDownLatch(10);
Таким образом, он станет равным 0, когда 10 потоков его уменьшат на 1 (это происходит после тестирования, но перед уходом домой). В итоге в консоли вывод такой (он может варьироваться, но главное, что строка «все сдали, зачет окончен» стоит после всех 10 выводов строки «в процессе тестирования»):
в процессе тестирования в процессе тестирования в процессе тестирования в процессе тестирования в процессе тестирования пошел домой пошел домой пошел домой в процессе тестирования пошел домой в процессе тестирования пошел домой пошел домой в процессе тестирования в процессе тестирования в процессе тестирования пошел домой все сдали, зачет окончен пошел домой пошел домой пошел домой
Учитель ждет, пока все студенты сдадут тест (при этом после сдачи теста студент не ждет, а идет домой). А сам учитель прекращает ожидание после того, как 10 тестов сданы (независимо от того, сколько студентов успело уйти домой, главное — сдача теста).
Если убрать команду countDownLatch.await(), то позиция строки «все сдали, зачет окончен» может быть любой.
Итоги
Мы рассмотрели CountDownLatch, код есть на GitHub. О CyclicBarrier читайте тут.