Back to Synchronization Utilities: Latch, Barrier, Semaphore
Q6Question 6 of 6

CountDownLatch patterns?

Key Concepts
  • CountDownLatch is a one-shot gate initialised with a count; await() blocks until the count reaches zero.
  • Startup gate (count 1): many workers await, coordinator counts down once to release them all simultaneously.
  • Completion gate (count N): coordinator awaits, each worker counts down in a finally block when finished.
  • countDown() is idempotent after reaching zero and never blocks.
  • Always put countDown() in a finally block so a crashing worker still decrements.
  • Use timed await(long, TimeUnit) so the coordinator does not hang forever if a worker stalls.
  • A latch cannot be reset; use CyclicBarrier for repeated rounds or Phaser for dynamic registration.

Definition

CountDownLatch is a one-time gate: count 1 to release many waiters at once (startup), or count N to let one coordinator wait for all workers (completion); it never resets, so use CyclicBarrier or Phaser for repeated rounds.
  • CountDownLatch – one-shot synchronization aid initialized with a count.
  • await() – blocks the caller until the count reaches zero.
  • countDown() – decrements by one, never blocks; extra calls after zero are harmless.
  • Startup gate – count of 1, many workers await, one coordinator counts down.
  • Completion gate – count of N, coordinator awaits, N workers each count down.
  • No reset – once zero, stays zero forever; use CyclicBarrier or Phaser for reuse.

The startup gate is like a bank branch where ten tellers stand behind a shutter; the manager raises it once and all ten start serving at the same moment. The completion gate is the reverse: the supervisor waits while ten tellers each close their till and report done. The shutter is single-use. If you need it every day, use a CyclicBarrier.

Understanding the Concepts

What is CountDownLatch?

CountDownLatch is a one-shot synchronization aid initialized with a count. await() blocks the caller until the count reaches zero. countDown() decrements the count by one; it never blocks and is safe to call after the count reaches zero.

How does the startup gate pattern work?

Every worker calls startGate.await() at the top. All block. The coordinator calls countDown() once to fire the starting gun, releasing all workers simultaneously.

JAVA
package com.bank.sync;

import java.util.concurrent.*;

public class StartupGateDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startGate = new CountDownLatch(1);
        ExecutorService pool = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 10; i++) {
            final int id = i;
            pool.submit(() -> {
                try {
                    startGate.await();  // all 10 block here
                    System.out.println("Worker-" + id + " starting settlement");
                    doSettlement(id);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return null;
            });
        }

        System.out.println("Preparing resources...");
        Thread.sleep(1000);  // setup phase
        startGate.countDown();  // fire the starting gun: all 10 begin together
        pool.shutdown();
    }
}

How does the completion gate pattern work?

The count equals the number of workers. Each worker counts down in a finally block when it finishes.

JAVA
package com.bank.sync;

import java.util.concurrent.*;

public class CompletionGateDemo {
    public static void main(String[] args) throws InterruptedException {
        int workerCount = 10;
        CountDownLatch doneGate = new CountDownLatch(workerCount);
        ExecutorService pool = Executors.newFixedThreadPool(workerCount);

        for (int i = 0; i < workerCount; i++) {
            final int id = i;
            pool.submit(() -> {
                try {
                    doSettlement(id);
                } finally {
                    doneGate.countDown();  // ALWAYS in finally!
                }
            });
        }

        boolean allDone = doneGate.await(30, TimeUnit.SECONDS);
        if (allDone) {
            System.out.println("All settlements complete.");
        } else {
            System.err.println("Timeout: not all workers finished.");
        }
        pool.shutdown();
    }
}

Why must countDown be in a finally block?

If a worker throws an exception before decrementing, the coordinator waits forever (or until its own timeout). Putting countDown in a finally block ensures the count is decremented even if the worker crashes.

How do you prevent infinite waiting?

Always use the timed variant of await. Without a timeout, a crashed worker with a missed countDown causes the coordinator to hang indefinitely.

JAVA
// WRONG: may wait forever
doneGate.await();

// CORRECT: bounded wait
boolean allDone = doneGate.await(30, TimeUnit.SECONDS);
if (!allDone) {
    System.err.println("Timeout: " + doneGate.getCount() + " workers still running");
}

Can you combine startup and completion gates?

Yes. Use a startup gate (count 1) to release workers simultaneously, and a completion gate (count N) to wait for all workers to finish. This is the standard pattern for timing a concurrent test.

JAVA
CountDownLatch startGate = new CountDownLatch(1);
CountDownLatch doneGate = new CountDownLatch(10);

for (int i = 0; i < 10; i++) {
    pool.submit(() -> {
        try {
            startGate.await();     // wait for the gun
            doSettlement();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            doneGate.countDown();  // signal done
        }
    });
}

long start = System.nanoTime();
startGate.countDown();            // fire the gun
doneGate.await(30, TimeUnit.SECONDS);  // wait for all
long elapsed = System.nanoTime() - start;
System.out.println("All done in " + elapsed / 1_000_000 + " ms");

Why can a latch not be reset?

Once the count hits zero it stays zero forever. The countDown calls are idempotent after reaching zero. For repeated rendezvous, use CyclicBarrier (automatic reset after each trip) or Phaser (dynamic registration and deregistration of parties).

| Feature | CountDownLatch | CyclicBarrier | Phaser |
|---------|---------------|---------------|--------|
| Reset | No | Automatic | Automatic |
| Dynamic parties | No | No | Yes |
| Action on trip | No | Optional Runnable | Yes |
| Use case | One-time event | Repeated rounds | Dynamic phases |

What are common interview follow-up questions?

  • Can multiple threads call countDown on the same latch? Yes, any thread can call countDown. Each call decrements by one.
  • What happens if you call countDown more times than the initial count? Nothing. Extra calls have no effect once the count reaches zero.
  • How does CyclicBarrier handle a broken barrier? If any party throws an exception or times out, the barrier breaks and all waiting parties receive BrokenBarrierException.

Visual Overview

DIAGRAM
  Startup Gate (count = 1):
  Worker 1: ─── await() ──┐
  Worker 2: ─── await() ──┤
  Worker 3: ─── await() ──┤── all blocked
  Worker N: ─── await() ──┘
                           │
  Coordinator: countDown()─┘── all released simultaneously

  Completion Gate (count = N):
  Worker 1: ─── work ─── countDown() ──┐
  Worker 2: ─── work ─── countDown() ──┤
  Worker 3: ─── work ─── countDown() ──┤── count reaches 0
  Worker N: ─── work ─── countDown() ──┘
                                        │
  Coordinator: ─── await() ─────────────┘── unblocked

Key Takeaway

CountDownLatch is a one-shot synchronization aid. The startup gate creates a latch with count 1, workers await, and the coordinator counts down once to release them all simultaneously. The completion gate creates a latch with count N, each worker counts down in a finally block, and the coordinator awaits until all are done.

Two practices matter: always put countDown in a finally block, and always use the timed await so the coordinator does not hang forever.

Because a latch cannot be reset, for repeated rounds use CyclicBarrier or Phaser.

Remember one sentence: CountDownLatch is a single-use gate: count one to release many waiters together, or count N to let one coordinator wait for all workers, and use CyclicBarrier for repeated rounds.

---

Share
Use ← → arrow keys to navigate