Иногда возникают ситуации, когда поток необходимо приостановить до наступления какого-то события (или событий). Для этих целей в Java предусмотрен класс CountDownLatch (представлен в Java 1.5). Объект класса CountDownLatch создается со счетчиком событий, который уменьшается по мере возникновения событий, как только счетчик будет равен 0, блокировка потока будет снята. Во время ожидания снятия блокировки может возникнуть исключения вида InterruptedException, поэтому вызов метода ожидания должен быть обернут в блок try/catch или в самом методе должно быть объявлено, что в нем может возникнуть исключение.
Если обратиться к документации класса CountDownLatch или просто посмотреть исходный код, то можно увидеть конструктор, который в качестве параметра принимает количество событий (в формате int), которые должны произойти для снятия блокировки.
1 2 3 4 |
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } |
Для ожидания окончания блокировки в классе CountDownLatch используется метод await, который представлен в двух формах. В первой – ожидание длится до тех пор, пока отсчет не достигнет 0:
1 2 3 |
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } |
Во второй форме метода await – ожидание длится только определенный период времени. Если период времени истек, то метод завершится и вернет false, если же счетчик событий достигнет 0, то метод вернет true:
1 2 3 4 |
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } |
Рассмотрим простой пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package ru.javanerd; import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String args[]) { //Создание экземпляра блокировки CountDownLatch latch = new CountDownLatch(3); System.out.println("Запуск отдельного потока"); //Создание экземпляра отдельного потока new CustomThread(latch); try { //Ожидание снятия блокировки latch.await(); } catch (InterruptedException ex) { System.out.println("Ошибка: " + ex.getMessage()); } System.out.println("Завершение отдельного потока"); } } //Класс отдельного потока class CustomThread implements Runnable { private final CountDownLatch latch; //Конструктор класса CustomThread(CountDownLatch latch) { this.latch = latch; //Запуск потока new Thread(this).start(); } //Метод выполнение потока @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(i); //Уменьшение счетчика срабатывания блокировки latch.countDown(); } } } |
Результат выполнения:
1 2 3 4 5 |
Запуск отдельного потока 0 1 2 Завершение отдельного потока |
В методе main() создается блокировка latch с обратным отсчетом 3. Далее создается экземпляр класс CustomThread и включается блокировка latch.await(). Блокировка latch.await() обязательно должна быть обернута блоком try/catch.
Класс CustomThread реализует интерфейс Runnable. В конструктор класса передается блокировка (latch) и запускается сам поток. В цикле for метода run происходит уменьшение счетчика срабатывания блокировки latch.countDown().
Исходный код доступен на GitHub