skip to content

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

level: seniorimportance: should knowfreq 45%

answer

  1. await() throws InterruptedException — restore the flag
  2. Timed await: latch returns false, barrier throws TimeoutException
  3. BrokenBarrierException = a party failed; reset() to reuse
  4. countDown() in finally so exceptions still release
  5. State getters are racy — diagnostics only

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.

solid answer

~40 s

Both CountDownLatch.await() and CyclicBarrier.await() are blocking and throw InterruptedException, so you must handle interruption and typically restore the interrupt status with Thread.currentThread().interrupt() rather than swallowing it. Prefer the timed overloads: latch.await(timeout, unit) returns false on timeout so you can recover instead of waiting forever for a worker that died; barrier.await(timeout, unit) throws TimeoutException, which also breaks the barrier. CyclicBarrier additionally throws BrokenBarrierException: when any party is interrupted, times out, the barrier is reset(), or the barrier action throws, the barrier enters a broken state and every other waiter gets this exception so nobody is stranded. After breakage you must reset() to reuse it. Always pair latch.countDown() with a finally block so an exception in a worker still releases the waiter. Check isBroken()/getCount() only for diagnostics, never as control logic, because they race.

code

java · 20 lines
java
// Bounded, interrupt-correct wait on a latch
try {
    if (!latch.await(30, TimeUnit.SECONDS)) {
        log.warn("workers did not finish in time");
        // recover: fail fast / retry / proceed degraded
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // restore status, do not swallow
    throw new CancellationException("interrupted while awaiting");
}

// Barrier side
try {
    barrier.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
} catch (BrokenBarrierException | TimeoutException e) {
    // another party failed/timed out; the barrier is now broken
    barrier.reset(); // before reusing it
}

go deeper

for a junior

Knows await() can be interrupted and that you should not ignore InterruptedException.

for a middle

Uses timed awaits and finally-guarded countDown(), and knows the barrier can throw BrokenBarrierException.

for a senior

Explains all four barrier-breaking causes, the interrupt-flag restore convention, and why state getters are diagnostics only; writes resilient waiting code.

for a principal

Establishes cancellation/timeout policy across the codebase, reasons about liveness and partial-failure recovery, and chooses primitives (Phaser/structured concurrency) that fail safely.

## Why failure handling matters Blocking on a synchronizer means a thread is **suspended until signalled**. If the signal never comes — a worker crashed, was cancelled, or deadlocked — a naive `await()` waits **forever**. Robust code plans for these failures. ## InterruptedException (both classes) `await()` is **interruptible**: if another thread calls `t.interrupt()` on the waiting thread `t`, `await()` throws `InterruptedException` and clears the thread's interrupt flag. - **Interrupt** is Java's cooperative cancellation signal — a request for the thread to stop what it's doing. - The convention: either propagate the exception, or, if you catch it and can't propagate, **restore the flag** with `Thread.currentThread().interrupt()` so higher-level code still sees the cancellation. **Swallowing** it (empty catch) is a classic bug that makes threads uncancellable. ## Timeouts — bound the wait Prefer the **timed** overloads so a missing signal can't hang you: - `CountDownLatch.await(timeout, unit)` returns a **boolean**: `true` if the latch opened, `false` if the time elapsed first. You then decide how to recover (log, fail fast, retry). - `CyclicBarrier.await(timeout, unit)` throws **`TimeoutException`** on expiry, and that expiry **breaks the barrier** for everyone else. ## BrokenBarrierException (CyclicBarrier only) A `CyclicBarrier` has a **broken** state. It breaks when any of these happen to a participant: it is **interrupted** while waiting, its **timed await times out**, someone calls **`reset()`**, or the **barrier action throws**. Once broken, every other thread waiting (and any that arrive before a reset) throws **`BrokenBarrierException`** instead of hanging. This is a *feature*: it guarantees no party is silently stranded when the group can no longer complete. To reuse a broken barrier you must call **`reset()`**, which itself breaks any in-progress wait and returns the barrier to its initial state. ## CountDownLatch has no 'broken' notion A latch can't break; if too few `countDown()` calls happen it simply never opens. That's why the **timed await** and a **finally-guarded countDown()** are the two essential safety nets: ``` try { doWork(); } finally { latch.countDown(); } ``` Without the `finally`, an exception in `doWork()` skips `countDown()` and the waiter hangs. ## Diagnostics vs control logic `getCount()`, `isBroken()`, and `getNumberWaiting()` are **racy snapshots** — fine for logging, dangerous as the basis for branching, because the value can change immediately after you read it. Drive logic off the `await()` return / exceptions, not these getters. ## Putting it together A resilient consumer: use a **timed await**, **handle InterruptedException** (restore the flag), **catch BrokenBarrierException/TimeoutException** for barriers, guard signalling in **finally**, and treat state getters as observability only.

  • Why call Thread.currentThread().interrupt() in the catch block?
    await() clears the interrupt flag when it throws InterruptedException. Restoring it preserves the cancellation request so higher-level code can still observe and act on it.
  • What four situations break a CyclicBarrier?
    A waiting party is interrupted, a timed await times out, someone calls reset(), or the barrier action throws an exception.

saying these in an interview costs you the question

  • Swallowing InterruptedException with an empty catch
  • Using the untimed await() everywhere, so one dead worker hangs the system forever
  • Calling countDown() only on the happy path, not in finally
  • Branching on getCount()/isBroken() as if they were stable
  • Believing CountDownLatch can throw BrokenBarrierException (only CyclicBarrier breaks)

context