Короткий ответ
CountDownLatch позволяет одному или нескольким потокам ждать, пока набор других потоков не выполнит операции: он работает на счётчике, который уменьшается вызовами countDown(), а ожидающие блокируются в await(), пока счётчик не станет нулевым. CyclicBarrier заставляет фиксированную группу потоков встретиться в одной точке и после отпускания сбрасывается, поэтому пригоден для повторного использования. Semaphore хранит счётчик разрешений и через acquire()/release() ограничивает число потоков, одновременно работающих с ресурсом.
Как это работает подробнее
CountDownLatch создаётся с начальным значением счётчика и используется один раз: когда счётчик достигает нуля, все ожидающие потоки освобождаются, но восстановить latch нельзя. Классический сценарий — главный поток ждёт, пока несколько фоновых задач загрузят данные.
CyclicBarrier создаётся на фиксированное число участников (parties); опционально в конструктор передаётся Runnable — барьерное действие, которое выполняется, когда все потоки достигли точки. После отпускания барьер автоматически сбрасывается и готов к следующей «волне».
- Semaphore — родственный инструмент: acquire() занимает разрешение, release() возвращает его, тем самым ограничивая число одновременно работающих потоков (например, размер пула соединений).
- Latch и barrier координируют момент встречи или наступление события, тогда как semaphore задаёт лимит одновременного доступа.
Пример кода
import java.util.concurrent.*;
public class LatchBarrierExample {
public static void main(String[] args) throws Exception {
// CountDownLatch: главный поток ждёт завершения 3 задач
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(latch::countDown).start();
}
latch.await(); // ждём, пока счётчик не станет 0
// CyclicBarrier: 3 потока стартуют «волнами» вместе
CyclicBarrier barrier = new CyclicBarrier(3,
() -> System.out.println("волна готова"));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
barrier.await();
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}