skip to content

CountDownLatch & CyclicBarrier

CountDownLatch is a one-shot gate that opens when the count hits zero; CyclicBarrier is a reusable rendezvous where N parties wait for each other. Interviewers ask for the difference almost every time either one comes up.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is a CountDownLatch and how do you use it?

level: juniorimportance: must knowfreq 70%

answer

  1. One-shot counting gate
  2. await() blocks, countDown() decrements
  3. Released at zero, never resets
  4. Start gate (count=1) vs wait-for-completion (count=N)
  5. Any thread can countDown()

basics

~20 s

A CountDownLatch is a one-time gate. You start it with a count. Threads call await() to wait; other code calls countDown() to lower the count. When it hits zero, every waiter is released and can continue.

solid answer

~40 s

CountDownLatch is a one-shot synchronizer from java.util.concurrent. You construct it with an integer count. One or more threads call await(), which blocks until the count reaches zero. Other threads call countDown(), each call decrementing the count by one. When it hits zero, all waiting threads are released at once and any later await() returns immediately. The classic use is making a main thread wait until N worker threads have each signalled completion, or holding N workers at a starting gate until the main thread counts down a single latch to start them together. It is single-use: once the count reaches zero it stays there and cannot be reset. countDown() can be called by any thread, and the threads that count down need not be the threads that await.

code

java · 13 lines
java
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    int id = i;
    new Thread(() -> {
        try {
            doWork(id);
        } finally {
            latch.countDown();   // signal this worker is done
        }
    }).start();
}
latch.await();                   // main thread blocks until count == 0
System.out.println("all workers finished");

go deeper

for a junior

Knows it is a counter you await() on and countDown(), released at zero, used to wait for workers.

for a middle

Distinguishes the start-gate (count=1) vs wait-for-completion (count=N) patterns and uses a timeout-bounded await; puts countDown() in finally.

for a senior

Explains the happens-before visibility guarantee and the one-shot semantics, and chooses latch vs barrier deliberately.

for a principal

Designs coordination primitives, weighs latch against CompletableFuture/phaser-based approaches, and reasons about failure modes (dead worker hangs the waiter) and observability.

## The problem it solves In concurrent programs you often need one thread to **wait until a set of events has happened** before continuing. For example, a main thread may launch several worker threads and must not proceed until all workers have finished initialising. Without a tool you'd resort to fragile loops checking a shared flag. `CountDownLatch` is a purpose-built tool for this. ## What it is `java.util.concurrent.CountDownLatch` is a **synchronizer** — a small helper that coordinates the timing of threads. Think of it as a **counter with a gate**: - You create it with a starting number: `new CountDownLatch(3)`. - Threads that want to wait call **`await()`**. This **blocks** the calling thread (it parks, using no CPU) until the counter reaches zero. - Any code calls **`countDown()`**, which **decreases the counter by one**. - The instant the counter reaches **zero**, every thread blocked in `await()` is **released simultaneously**, and any future `await()` returns immediately. ### Key terms defined - **Block / await**: the thread is suspended by the OS scheduler and consumes no CPU while waiting; it resumes when signalled. - **Synchronizer**: a class whose only job is to control when threads may proceed relative to each other. - **One-shot**: the counter only goes down, never resets. Once at zero it stays at zero forever. ## The two classic patterns 1. **Wait for completion** — count = N (number of workers). The coordinator calls `await()`; each worker calls `countDown()` when it finishes. The coordinator wakes only after all N have finished. 2. **Start gate** — count = 1. All workers call `await()` first, blocking at a starting line; the coordinator does setup then calls `countDown()` once, releasing every worker at the same moment for a synchronized start. ## Important details - `countDown()` can be called by **any** thread, and **never blocks** — it just decrements. Extra calls below zero are harmless no-ops. - The threads doing `countDown()` do **not** have to be the threads doing `await()`. - `getCount()` returns the current count (useful for debugging, not for control logic). - `await(timeout, unit)` waits at most a bounded time and returns `true` if the latch opened, `false` on timeout — important so you never wait forever if a worker dies. - **Memory visibility**: actions in a thread before its `countDown()` *happen-before* actions after a successful `await()` in another thread. So results published by workers are safely visible to the waiter. ## When NOT to use it If you need to reuse the barrier across multiple rounds, `CountDownLatch` cannot reset — use `CyclicBarrier` (reusable) instead.

  • What happens if you call await() after the count has already reached zero?
    It returns immediately without blocking, because the latch is already open and stays open permanently.
  • Can the same threads that await() also call countDown()?
    Yes, there is no rule tying the two. countDown() is independent of await(); any thread can call either.

A relay race official holding runners at the line: when the official's count reaches zero (the starting gun), everyone is released at once. The gun can only be fired once — you can't 'un-fire' it for the next race.

saying these in an interview costs you the question

  • Claiming a CountDownLatch can be reset or reused after reaching zero
  • Thinking await() returns when count drops by one rather than when it reaches zero
  • Believing only the waiting threads may call countDown()
  • Forgetting countDown() in a finally block, so a thrown exception leaves the waiter hung forever

context

open as a page

What is a CyclicBarrier and what is its barrier action?

level: middleimportance: must knowfreq 62%

basics

~20 s

A CyclicBarrier is a reusable meeting point for a fixed number of threads. Each thread calls await() and blocks until all of them arrive. Then they all proceed, and the barrier resets so it can be used again for the next round.

open as a page

What are the key differences between CountDownLatch and CyclicBarrier, and when would you choose each?

level: seniorimportance: must knowfreq 78%

basics

~20 s

A CountDownLatch is one-shot: any thread counts it down and waiters proceed at zero. A CyclicBarrier is reusable: a fixed set of threads wait for each other, then all go together and it resets. Use a latch for a single start/done signal, a barrier for repeated lockstep rounds.

open as a page

How should you handle interruption, timeouts, and barrier breakage when waiting on these synchronizers?

level: seniorimportance: should knowfreq 45%

basics

~20 s

await() can throw InterruptedException, so handle it and usually re-set the interrupt flag. Prefer the timed await so a dead worker can't hang you forever. For CyclicBarrier also catch BrokenBarrierException, which means another party failed and the barrier is now broken.

open as a page

When do CountDownLatch and CyclicBarrier fall short, and what would you reach for instead?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

They fall short when the number of participants changes over time or when you want to compose async results without blocking threads. For dynamic, multi-phase coordination use Phaser; for non-blocking result composition use CompletableFuture or structured concurrency.

open as a page