skip to content

You catch an InterruptedException in a method that cannot propagate it. How should you handle it correctly, and why is swallowing it wrong?

level: seniorimportance: must knowfreq 80%

answer

  1. throwing InterruptedException CLEARS the flag
  2. two valid options: propagate OR restore-and-stop
  3. restore = Thread.currentThread().interrupt() in the catch
  4. swallowing loses the cancellation signal
  5. restore AND unwind — don't restore then keep blocking

basics

~10 s

Don't ignore it. Either rethrow it, or — if you can't — call Thread.currentThread().interrupt() to set the flag again so callers up the stack still know the thread was asked to stop.

solid answer

~50 s

There are two correct responses to InterruptedException, and silently catching it is never one of them. Option one, preferred: propagate it — declare throws InterruptedException and let the caller decide. Option two, when you genuinely cannot propagate (e.g. you're implementing Runnable.run(), which has no throws clause, or an interface method that forbids it): restore the interrupt status by calling Thread.currentThread().interrupt() inside the catch block, then return or break out of your work. The reason matters: when a blocking method throws InterruptedException, it has already cleared the interrupt flag. If you catch it and do nothing, the flag is gone, so code higher up the stack — including thread pools and loops — can no longer detect that cancellation was requested, and the thread keeps running as if nothing happened. Restoring the flag preserves the cancellation signal for everyone above you. Logging and exiting is fine; logging and continuing as normal is the bug.

code

java · 11 lines
java
public void run() {
    try {
        while (true) {
            Object item = queue.take();   // blocking; may throw InterruptedException
            process(item);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // restore the flag the throw cleared
        // fall through to return -> worker stops cleanly, signal preserved
    }
}

go deeper

for a junior

Knows you should not leave the catch block empty and that interruption means 'try to stop.'

for a middle

States the two correct options and can write Thread.currentThread().interrupt() in a Runnable's catch block.

for a senior

Explains that the throw clears the flag, why restoring preserves the signal for callers/pools, and avoids the 'restore then keep blocking' subtlety.

for a principal

Defines team-wide cancellation policy across layers, ensures libraries don't swallow interrupts, and integrates the rule with executor shutdown and static-analysis gating.

## Setup: where InterruptedException comes from Blocking methods — `Thread.sleep`, `Object.wait`, `Thread.join`, `BlockingQueue.take/put`, `Lock.lockInterruptibly`, `Future.get`, etc. — are *interruptible*: if the thread's **interrupt status flag** is (or becomes) set while they're waiting, they abandon the wait and throw the checked `InterruptedException`. **Crucially, throwing it also clears the flag** — the JVM resets the interrupt status to `false` before the exception propagates. The reasoning: the exception itself now carries the "you were interrupted" signal, so the flag is consumed. This hand-off is the root of all the rules below: **the signal lives in exactly one place at a time** — either in the flag, or in the in-flight exception. If you catch the exception and don't act, the signal is lost entirely. ## The two correct handlers ### 1. Propagate (preferred) Let the exception travel up to a caller who knows what to do: ```java void doStuff() throws InterruptedException { blockingCall(); // may throw; we just let it bubble up } ``` This keeps the cancellation contract intact: each layer that can be cancelled declares it, and the top-level owner of the thread (the pool, the main loop) ultimately handles shutdown. ### 2. Restore the flag, then stop (when you cannot propagate) Some method signatures forbid checked exceptions — most famously `Runnable.run()`, `Comparator.compare()`, or any third-party interface. There you **must** catch it, but you must also **re-set the flag** so the signal isn't swallowed: ```java public void run() { try { blockingCall(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // RESTORE the cleared flag return; // and stop the work } } ``` Now code above you — loop guards using `isInterrupted()`, the executor that ran this task, `Future.cancel` logic — can still observe that an interrupt happened. ## Why swallowing is a real bug ```java try { Thread.sleep(1000); } catch (InterruptedException e) { /* ignored */ } // WRONG ``` After this, the flag is `false` (cleared by the throw) **and** you discarded the exception. The interrupt has vanished. A surrounding `while (!isInterrupted())` loop will never exit; an `ExecutorService.shutdownNow()` that interrupted this task will think it ignored the request; the application can hang on shutdown. This is one of the most common concurrency defects, and tools like SpotBugs/Error Prone flag it (`DM_INTERRUPTED` / "InterruptedExceptionSwallowed"). ## Edge details - **Don't swallow but keep looping** is also wrong: restoring the flag is pointless if you then ignore it and continue the same blocking work. Restore *and* unwind. - **Wrapping** is acceptable if you preserve the signal: `Thread.currentThread().interrupt(); throw new RuntimeException(e);` (rare; usually just restore + return). - **In a loop reading a queue**, the idiom is: catch `InterruptedException`, restore the flag, and `break` out of the loop to terminate the worker. ## Terms - **Propagate:** rethrow / let the checked exception travel up via `throws`. - **Restore the flag:** call `Thread.currentThread().interrupt()` to re-set the status the throw cleared. - **Swallow:** catch and discard — the anti-pattern.

  • Why does restoring the flag matter if your method already returns right after?
    Because callers above you — loops, the executor, Future.cancel bookkeeping — inspect the interrupt status to decide whether to also shut down. Returning ends only your frame; restoring the flag lets the whole call stack and the thread's owner still see that cancellation was requested.
  • Is it ever acceptable to swallow InterruptedException?
    Almost never. The one defensible case is a thread you fully own that is about to die anyway and whose interrupt nothing else observes — and even then restoring the flag is cheaper than reasoning about whether it's safe. Treat 'swallow' as always wrong by default.

saying these in an interview costs you the question

  • Catching InterruptedException and doing nothing (empty catch block)
  • Logging it and then continuing the same work as if uninterrupted
  • Restoring the flag but then ignoring it and re-entering the blocking call
  • Assuming the flag is still set after the exception was thrown (it was cleared)

context