Wait for Workers Using CountDownLatch in Java
Problem
A CountDownLatch starts at a count; threads call countDown() as they finish, and any thread calling await() blocks until the count reaches zero.
Start three services on separate threads and let the main thread continue only after all three report that they're ready.
Java Program
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
String[] services = {"database", "cache", "search"};
CountDownLatch ready = new CountDownLatch(services.length); // counts down from 3
AtomicIntegerArray started = new AtomicIntegerArray(services.length);
for (int i = 0; i < services.length; i++) {
final int id = i;
new Thread(() -> {
started.set(id, 1); // pretend to start the service
ready.countDown(); // report "I'm up"
}).start();
}
ready.await(); // main blocks until all three have counted down
for (int i = 0; i < services.length; i++) {
System.out.println(services[i] + " started: " + (started.get(i) == 1));
}
System.out.println("All services ready, accepting requests");
}
}Output
Core Logic
The latch is a one-time gate: it opens when the number of countDown() calls reaches the starting count.
- 1
new CountDownLatch(3)matches the number of services. - 2Each worker marks its service as started, then calls
ready.countDown(). - 3
ready.await()blocks the main thread until the count is 0. - 4Main then prints the status of every service, which is complete by that point.
countDown().Key Point: Unlike join(), a latch doesn't care which threads count down or whether they keep running afterwards — it just counts events.
Key Concepts
Approach 2: A Single Start Signal
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class CountDownLatchStartSignal {
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1); // one "go" for everybody
CountDownLatch done = new CountDownLatch(4);
AtomicInteger finished = new AtomicInteger();
for (int i = 0; i < 4; i++) {
new Thread(() -> {
try {
startSignal.await(); // every runner waits at the start line
finished.incrementAndGet();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown();
}
}).start();
}
System.out.println("Runners finished before the signal: " + finished.get());
startSignal.countDown(); // release all four at once
done.await();
System.out.println("Runners finished after the signal: " + finished.get());
}
}
Output
Core Logic
A latch with a count of 1 works as a starting pistol: many threads wait on it and are released together.
- 1Each runner thread calls
startSignal.await()before doing its work. - 2Main checks that nothing has finished yet, then calls
startSignal.countDown()once. - 3A second latch,
done, lets main wait until all four have finished.
Key Point: Latches can't be reset; for a gate that's used repeatedly, use CyclicBarrier or Phaser.
Key Concepts
Interview Questions
What's the difference between CountDownLatch and CyclicBarrier?
A latch is used once and lets waiting threads continue when other threads count down. A barrier makes a fixed group of threads wait for each other and can be reused for several rounds.
What happens if a worker crashes before calling countDown?
The count never reaches zero and await() blocks forever. Call countDown() in a finally block, or use await(timeout, unit).