CountDownLatch
2 mins
CountDownLatch is a simple yet powerful synchronizer that allows one or more threads to wait until a specific number of operations have completed in other threads.
Source Code #
Implementation Mechanism #
CountDownLatch is an elegant implementation of AQS in Shared Mode. The state variable represents the initial count. When a thread calls countDown(), the state is decremented via CAS. Threads calling await() are parked in the wait queue until the state reaches exactly zero.
// Shared Mode acquisition in CountDownLatch.Sync
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// Shared Mode release in CountDownLatch.Sync
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
Canonical Usage #
When to use: Use CountDownLatch for one-time initialization or start-up coordination. It is NOT reusable; once the count reaches zero, the latch is open forever.
Common Patterns:
- Startup Synchronization: A main thread waits until several background services have initialized before proceeding.
- Simultaneous Start: Multiple threads wait on a “start gate” to begin an operation simultaneously.
- Unit Testing Concurrent Code: Ensuring multiple threads have finished their tasks before asserting results in a test.
// Main thread waits for 5 background tasks to finish initialization
CountDownLatch startGate = new CountDownLatch(5);
// In each background task
executor.execute(() -> {
try {
doInitialize();
} finally {
startGate.countDown(); // Signal that this task is ready
}
});
// Main thread blocks here until count reaches 0
startGate.await();
System.out.println("All tasks initialized. Proceeding...");
Difference from CyclicBarrier #
- One-time only:
CountDownLatchcannot be reset. - Unbalanced: One thread can wait for many other threads to
countDown(). In aCyclicBarrier, everyone must wait for everyone else.