| Property | CountDownLatch | CyclicBarrier |
| Reusable | No — count reaches zero and stays there | Yes — resets automatically after each barrier trip |
| Who counts | Any thread calls countDown() | Participating threads call await() |
| Who waits | Threads blocked on await() | All threads wait at the barrier until last arrives |
| Barrier action | None | Optional Runnable runs when barrier trips |
| Use case | Signal when N events have occurred | Synchronise N threads at a checkpoint |
| Exception handling | Interrupted threads propagate InterruptedException | One broken thread puts barrier in broken state for all |
A CountDownLatch is initialised with a count. Each call to countDown() decrements it. Threads blocked on await() are released when the count reaches zero.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LatchExample {
public static void main(String[] args) throws InterruptedException {
int workerCount = 5;
CountDownLatch latch = new CountDownLatch(workerCount);
ExecutorService pool = Executors.newFixedThreadPool(workerCount);
for (int i = 0; i < workerCount; i++) {
final int id = i;
pool.submit(() -> {
try {
System.out.println("Worker " + id + " starting");
Thread.sleep(200 + id * 100); // simulate work
System.out.println("Worker " + id + " done");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // always decrement, even on exception
}
});
}
System.out.println("Main thread waiting for all workers...");
latch.await(); // blocks until count reaches 0
System.out.println("All workers finished — proceeding!");
pool.shutdown();
}
}
Always call countDown() in a finally block so the latch is decremented even if the worker throws an exception. A missed countDown() will cause await() to block forever.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
CountDownLatch latch = new CountDownLatch(3);
// ... start workers ...
// Wait at most 5 seconds
boolean completed = latch.await(5, TimeUnit.SECONDS);
if (completed) {
System.out.println("All tasks finished in time");
} else {
System.out.println("Timeout — not all tasks completed");
}
// Check remaining count without waiting
System.out.println("Remaining: " + latch.getCount());
A common pattern: fan out N parallel fetches, then aggregate results once all are done.
import java.util.concurrent.*;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ParallelFetch {
record UserData(int id, String name) {}
public static List<UserData> fetchAll(List<Integer> userIds) throws InterruptedException {
List<UserData> results = new CopyOnWriteArrayList<>();
CountDownLatch latch = new CountDownLatch(userIds.size());
ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor(); // Java 21
for (int id : userIds) {
pool.submit(() -> {
try {
UserData data = fetchFromDb(id); // remote call
results.add(data);
} finally {
latch.countDown();
}
});
}
latch.await(10, TimeUnit.SECONDS);
pool.shutdown();
return results;
}
private static UserData fetchFromDb(int id) {
// simulate DB round-trip
return new UserData(id, "User-" + id);
}
}
All participating threads call await() and block until the last thread arrives. Then all are released simultaneously and the barrier resets for the next round.
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BarrierExample {
public static void main(String[] args) {
int parties = 3;
// Optional barrier action runs in the last thread to arrive
Runnable barrierAction = () ->
System.out.println("--- All threads reached barrier; starting next phase ---");
CyclicBarrier barrier = new CyclicBarrier(parties, barrierAction);
ExecutorService pool = Executors.newFixedThreadPool(parties);
for (int phase = 1; phase <= 3; phase++) {
final int p = phase;
for (int i = 0; i < parties; i++) {
final int id = i;
pool.submit(() -> {
try {
System.out.printf("Thread %d phase %d working%n", id, p);
Thread.sleep((long)(Math.random() * 300));
barrier.await(); // wait for all peers
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
});
}
}
pool.shutdown();
}
}
The CyclicBarrier resets automatically after each barrier trip, making it ideal for iterative algorithms (parallel merge sort phases, Monte Carlo simulation rounds, game loop tick synchronisation).
If one thread times out or is interrupted while waiting, the barrier enters a broken state. All threads waiting (or about to wait) receive BrokenBarrierException. Call barrier.reset() to recover — but only if no threads are currently waiting.
import java.util.concurrent.*;
CyclicBarrier barrier = new CyclicBarrier(3);
Runnable task = () -> {
try {
// Timed wait — prevents hanging if a peer crashes
barrier.await(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("Timed out waiting at barrier");
} catch (BrokenBarrierException e) {
System.out.println("Barrier broken by another thread");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// Check barrier state
System.out.println("Broken: " + barrier.isBroken());
System.out.println("Parties waiting: " + barrier.getNumberWaiting());
// Reset (only safe when no threads are waiting)
if (barrier.isBroken()) {
barrier.reset();
}
Calling barrier.reset() while threads are waiting causes those threads to receive BrokenBarrierException. Only reset when the barrier is not in active use.
| Scenario | Use |
| Wait for N services to start before accepting traffic | CountDownLatch |
| Wait for N tasks to complete before proceeding once | CountDownLatch |
| Signal a gate: one thread opens it, N threads rush through | CountDownLatch(1) |
| Parallel algorithm with multiple synchronisation rounds | CyclicBarrier |
| Run a barrier action (merge partial results) after each phase | CyclicBarrier + barrierAction |
| Game loop: all players must submit moves before next tick | CyclicBarrier |