Daniel Mitterdorfer

Handling InterruptedException Properly

Stop

Image by Juan Antonio CapĆ³ Alonso; license: CC

InterruptedException is among the most misunderstood exceptions in the JDK. How often did we come across exception handlers like this one:

public void enqueue(Object value) {
  try {
    queue.put(value);
  } catch (InterruptedException e) {
    // ignore
  }
}

Or even:

public void enqueue(Object value) {
  try {
    queue.put(value);
  } catch (InterruptedException e) {
    throw new RuntimeException(e);
  }
}

In most cases, these handler implementations are wrong.

Stopping a Thread in Java

To understand why these exception handlers are wrong, we have to know the purpose of this exception. As its name indicates, some method has been interrupted during execution. In Java, cancellation of a running thread is cooperative. That means that we cannot force a thread to terminate from the outside 1 but instead we can just request that it interrupts itself and rely that the code executed within a thread will notice the request and act accordingly. To achieve that, each thread manages a flag, the interruption status. It can be set with Thread#interrupt() and reset with Thread#interrupted(). Additionally, Thread#isInterrupted() is used to check the interruption status.

Let’s suppose a user wants to cancel a background operation running in a dedicated thread. To do so, client code calls interrupt() on this thread. Assuming that the background operation is in progress, i.e. it has been started but it has not yet finished, the thread can be in one of those states:2

  • RUNNABLE: That means code is currently being executed or the corresponding thread is at least eligible for scheduling.
  • BLOCKED, WAITING or TIMED_WAITING: Loosely speaking, the thread does not make any progress, because it is waiting on some event to occur like acquisition of a monitor or a timeout.

If the thread is in RUNNABLE state, it is rather simple to respond properly to interruption. We can periodically check the status of the interrupted flag with Thread#isInterrupted():

class ComplicatedCalculator implements Runnable {
  @Override
  public void run() {
    while (!Thread.currentThread.isInterrupted()) {
      // calculate something here
    }
  }
}

ComplicatedCalculator periodically checks the interruption status of the thread it is currently running in by calling Thread.currentThread.isInterrupted(). This ensures that the corresponding thread can terminate timely upon a request to interrupt itself.

However, what shall we do if the code is not in the state RUNNABLE, i.e. in one of the states BLOCKED, WAITING or TIMED_WAITING? Consider this simulation that recalculates its state roughly every second. There is InterruptedException again:

class Simulation implements Runnable {
  @Override
  public void run() {
    while (!Thread.currentThread.isInterrupted()) {
      // ... simulation calculations here ...
      try {
        Thread.sleep(1000);
	  } catch (InterruptedException e) {
	    // empty
	  }
	}
  }
}

To understand what’s wrong here, consider you are the implementor of Thread#sleep(). How should you indicate that the method did not return normally but has been interrupted? You could use a special return code, but this is brittle because it places the burden of checking the return value on the caller. Instead, Thread#sleep() resets the thread’s interrupt status and throws InterruptedException. This means, whenever InterruptedException is thrown, some part of the system requested to terminate the current thread while it was not in RUNNABLE state, and we should get out of the way as quickly as possible.

In general, there are multiple strategies on how to handle InterruptedException:

  • Rethrow: Classes which implement blocking operations like BlockingQueue can just rethrow InterruptedException.
  • Catch, restore and terminate: Classes that implement an existing interface like Runnable cannot rethrow InterruptedException as it is a checked exception and needs to be included in the method signature. So they need another means to communicate interruption to code higher up the call stack. It should catch InterruptedException, restore the interruption status and terminate as soon as possible.
  • Catch and terminate: This is a variation of the strategy above. Subclasses of Thread do not need to restore the interruption flag as they are already at the top of the call stack and there is no one else that cares about the interruption status.

There are cases which justify other strategies, but for the majority of situations, these three strategies work fine.

Canonical Solution

To conclude the simulation example, support for interruption can be implemented using the “catch, restore and terminate” strategy as follows:

class Simulation implements Runnable {
  @Override
  public void run() {
    while (!Thread.currentThread.isInterrupted()) {
      // ... simulation calculations here ...
      try {
        Thread.sleep(1000);
	  } catch (InterruptedException e) {
	    // restore interruption status of the corresponding thread
	    Thread.currentThread.interrupt();
	  }
	}
  }
}

The run loop will terminate quickly when the corresponding thread is interrupted either in Thread#sleep() or immediately after calculation hence guaranteeing timely termination of the corresponding thread. By the way: If the simulation would have contained just an empty catch block, it is almost impossible to ever interrupt it. As Thread#sleep() will reset the interruption status flag when it throws InterruptedException, the check at the beginning of the loop will hardly ever have a chance to observe that the interruption status flag is set.

Cancellation in Thread Pools

Java 5 has brought us ExecutorService, which means that client code does not have a Thread reference to call interrupt() on it. Instead, ExcecutorService#submit(Runnable) returns a Future. To request interruption, just invoke Future#cancel(true). Here’s a minimalistic example:

Simulation simulation = new Simulation();
ExecutorService e = Executors.newFixedThreadPool(4);
Future<?> f = e.submit(simulation);
// ...
// now cancel the running simulation
f.cancel(true);

This is not the only situation where interruption occurs in a thread pool: ExecutorService#shutdownNow() will terminate all running tasks by calling Thread#interrupt() internally on pool threads. So proper handling of InterruptedException is also important to shutdown a thread pool properly.


  1. There was a misguided effort in the early days of Java with Thread#stop(). An Oracle technote describes its problems more deeply. ↩︎

  2. Actually, the Java thread state model defines some more states, but we don’t bother about it for this article. ↩︎