A CountDownLatch is a synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. It’s initialized with a given count, and the await() methods block until the current count reaches zero due to invocations of the countDown() method.
Here is a practical guide on how to coordinate threads using CountDownLatch.
1. Basic Coordination Pattern
The most common use case is having a main thread wait for several worker threads to finish their tasks.
package org.kodejava.util.concurrent;
import java.util.concurrent.CountDownLatch;
public class LatchCoordination {
public static void main(String[] args) throws InterruptedException {
int numberOfWorkers = 3;
// 1. Initialize with the number of events to wait for
CountDownLatch latch = new CountDownLatch(numberOfWorkers);
for (int i = 0; i < numberOfWorkers; i++) {
new Thread(new Worker(latch, "Worker-" + i)).start();
}
System.out.println("Main thread is waiting...");
// 2. Wait until the count reaches zero
latch.await();
System.out.println("All workers finished. Main thread proceeding.");
}
}
class Worker implements Runnable {
private final CountDownLatch latch;
private final String name;
Worker(CountDownLatch latch, String name) {
this.latch = latch;
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " is performing work...");
Thread.sleep(1000); // Simulate work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 3. Crucial: Always decrement the count in a finally block
latch.countDown();
System.out.println(name + " finished. Remaining: " + latch.getCount());
}
}
}
2. Key Methods to Remember
new CountDownLatch(int count): Sets the initial count. This count cannot be reset. If you need to reset the count, consider using aCyclicBarrier.countDown(): Decrements the count of the latch, releasing all waiting threads if the count reaches zero.await(): Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted.await(long timeout, TimeUnit unit): A safer version that waits for a specific duration. It returnstrueif the count reached zero andfalseif the waiting time elapsed before the count reached zero.
3. Advanced Use: The “Starting Gun”
You can also use a CountDownLatch with a count of 1 as a “starting gun” to release many threads at the exact same moment (useful for load testing).
CountDownLatch startSignal = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
startSignal.await(); // All threads stall here
// Do concurrent work...
} catch (InterruptedException e) { /* handle */ }
}).start();
}
// All threads start simultaneously when this is called
startSignal.countDown();
Best Practices
- Always use
finally: PlacecountDown()inside afinallyblock to ensure the latch is decremented even if a worker thread encounters an exception. Otherwise, the waiting thread might block indefinitely. - Don’t reuse: Once a
CountDownLatchreaches zero, it cannot be reused. If your algorithm requires repeating the synchronization point, use aCyclicBarrierorPhaser. - Check the count: You can use
latch.getCount()for debugging or monitoring, but don’t use it to make control flow decisions in production code as it is volatile.
