The java.lang.InterruptedException: an introduction to the Java developer

Marcio Endo
January 28, 2024

Suppose you are writing to a file in a Java application and, unfortunately, the disk is full.

Regardless of your view on Java's exceptions, checked exceptions in particular, it is easy to understand why the following will fail with an IOException:

try {
  var path = Path.of("/tmp/my-file.txt");
  Files.writeString(path, "Hello world!");
} catch (IOException e) {
  // The disk is full.
  // Log or display error to the user
}

But what about the following code?

try {
  TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
  // what's an InterruptedException?
}

What can "go wrong" in my code so that I must catch an InterruptedException? And why is that some methods throw it while others (most) do not?

In short, what's an InterruptedException?

In this blog post we will try to answer those questions.

Let's begin.

Threads, threads everywhere

The first thing to know is that the InterruptedException exception is directly related to Java threads.

So, before we talk about the exception itself, we must discuss threads in Java.

Everything runs in a thread

In a Java application any and all of the code runs in a thread.

You might not have started a thread yourself. But if your code is running then then at least one thread was started for you.

Take, for example, the following Java program:

public class Example00 {
  public static void main(String[] args) {
    log("BEGIN");
    
    log("END");
  }
  
  private static void log(String msg) {
    Thread currentThread;
    currentThread = Thread.currentThread();

    String name;
    name = currentThread.getName();

    System.out.format("%6s: %s%n", name, msg);
  }
}

Notice that it does not start any Thread instance directly. Still, in the log method, it:

Let's run our program.

When executed, it prints:

  main: BEGIN
  main: END

So our program was executed by a thread named "main".

We did not explicitly start it.

The main thread is started by the JVM to run the main method of a Java application.

When does a Thread stop executing?

Depending on the kind of Java code you write, the following might surprise you.

It is common to design and code threads that run indefinitely.

In other words, it is common for the run method of a Java Thread or Runnable to be a variation of:

@Override
public void run() {
  while (true) {
    // do this thing repeatedly
    // for the entire life of the application...
  }
}

One example where this occurs is in servers.

A web server or a database server will start at least one such threads. Its job is to listen at a network socket waiting for incoming connections.

A cron server will also have one of such threads. The thread will be sleeping most of the time, waking up occasionally at the configured schedule.

Still, servers need to be shutdown eventually. And we want to do it in a orderly fashion. Meaning that the thread might need to do some cleaning up before it stops executing.

To address this issue, Java offers the Thread::interrupt method.

An example

Let's start by writing a Thread of our own that runs indefinitely.

We will use the following Runnable implementation:

public class Multiples01 implements Runnable {
  
  private final Random random = new Random();
  
  @Override
  public final void run() {
    while (true) {
      int next;
      next = random.nextInt();
      
      if (next <= 0) {
        continue;
      }
      
      if ((next % 111_222_333) != 0) {
        continue;
      }
      
      String found;
      found = Integer.toString(next);
        
      Logger.log(found);
    }
  }
  
}

The run method contains a while statement whose condition is always true. On each while iteration it gets a random int value. It prints the value if it is a positive multiple of 111_222_333.

The log method of our first example was extracted to a Logger class.

Running and interrupting our example

To execute our Runnable we write the following Java program:

public class Example01 {
  public static void main(String[] args) {
    Logger.log("BEGIN");
    
    Runnable task;
    task = new Multiples01();

    Thread thread;
    thread = Thread.ofPlatform().name("MULT1").start(task);
    
    thread.interrupt();
    
    Logger.log("END");
  }
}

It starts a platform thread named MULT1 with an instance of the class defined earlier.

It then immediately calls the interrupt method of the thread instance.

Here's the output of one of the runs:

  main: BEGIN
  main: END
 MULT1: 1668334995
 MULT1: 1334667996
 MULT1: 1112223330
 MULT1: 1890779661
 MULT1: 222444666
 MULT1: 1112223330
 MULT1: 111222333
 MULT1: 778556331
 MULT1: 333666999
 (...)

The program runs continuously and it does not stop.

It only stops executing when typing <CTRL-C> in the console.

So calling the Thread::interrupt method produced no effect in this particular example.

How come?

Responding to the Thread::interrupt method

In general terms, a thread will only stop executing when it returns from the run method.

So a thread will only stop executing in response to a Thread::interrupt invocation if you told the thread to do so.

Let's introduce a few changes to our Runnable class implementation:

public class Multiples02 implements Runnable {
  
  public volatile int count;
  
  private final Random random = new Random();
  
  @Override
  public final void run() {
    while (true) {
      int next;
      next = random.nextInt();
      
      if (next <= 0) {
        continue;
      }
      
      if ((next % 111_222_333) != 0) {
        continue;
      }
     
      String found;
      found = Integer.toString(next);
        
      Logger.log(found);

      count++;
      
      if (Thread.interrupted()) {
        break;
      }
    }
  }
  
}

The class now has a count instance variable that holds the number of values it has printed. It may overflow; but we expect the counting to stop before it does.

After the counter is updated, it checks if the executing thread has been interrupted. It does so by invoking the Thread::interrupted static method.

If the thread has been interrupted, then it breaks out of the while loop. By doing so, it effectively returns from the run method.

Running our updated Runnable class

To run our updated Runnable class we update our Java program to the following:

public class Example02 {
  public static void main(String[] args) {
    Logger.log("BEGIN");
    
    Multiples02 task;
    task = new Multiples02();

    Thread thread;
    thread = Thread.ofPlatform().name("MULT2").start(task);
    
    while (task.count < 5) {
      Thread.onSpinWait();
    }
    
    thread.interrupt();
    
    Logger.log("END");
  }
}

After we've started the MULT2 platform thread, we continuously check the count of printed values.

After 5 values have been printed, we interrupt the thread.

Let's see it in action. Here's the output of one of the runs:

  main: BEGIN
 MULT2: 1557112662
 MULT2: 2002001994
 MULT2: 1334667996
 MULT2: 2002001994
 MULT2: 778556331
  main: END

So our updated version properly responds to the thread being interrupted.

What about the InterruptedException?

Some methods are blocking. In other words, they will block the executing thread until it returns.

By this definition all methods are blocking.

When we specifically say that a method is blocking we usually mean that it might block indefinitely. Or, at least, it might not return right after it has been invoked.

Blocking methods

The BlockingQueue::take method is an example of a blocking method.

To illustrate, consider the following Java program:

public class Example03 {
  public static void main(String[] args) {
    Logger.log("BEGIN");

    BlockingQueue queue;
    queue = new LinkedBlockingQueue();
    
    try {
      queue.take();      
    } catch (InterruptedException e) {
      Logger.log("Interrupted!");
    }
    
    Logger.log("END");
  }
}

The program invokes the take method on an empty BlockingQueue.

When executed, this program blocks the main thread indefinitely:

  main: BEGIN

We need to forcefully stop the program execution by typing <CTRL-C> in the console.

Blocking methods and Thread::interrupt

As we have discussed so far, a thread will only stop executing when it returns from its run method.

Also, a blocking method may block the executing thread indefinitely.

So how can a thread that is blocked on a blocking method respond to it being interrupted?

The answer to this question is the InterruptedException class. A blocking method might throw an InterruptedException in response to the executing thread being interrupted.

Let's see an example.

A (sort of) web server

Let's write a server that takes requests from a queue:

public class WebServer implements Runnable {
  
  private final BlockingQueue<String> queue;

  WebServer(BlockingQueue<String> queue) {
    this.queue = queue;
  }
  
  @Override
  public final void run() {
    while (true) {
      try {
        String msg;
        msg = queue.take();
        
        Logger.log(msg);
      } catch (InterruptedException e) {
        Logger.log("Got interrupted!");
        
        break;
      }
    }
  }
  
}

To create an instance of this class we must provide a BlockingQueue instance.

This class will act as a server. So its run method has a while statement whose expression is the true boolean literal. In other words, the method is designed to run indefinitely.

The class waits for messages to be available in its queue. When a message is available it is logged.

If the thread gets interrupted, a "Got interrupted!" message is logged and it breaks out of the while loop.

Running our web server

We write the following Java program to run our "web server":

public class Example04 {
  public static void main(String[] args) throws InterruptedException {
    Logger.log("BEGIN");
    
    BlockingQueue<String> queue;
    queue = new LinkedBlockingQueue<>();

    Runnable task;
    task = new WebServer(queue);

    Thread server;
    server = Thread.ofPlatform().name("WEB").start(task);
    
    Random random;
    random = new Random();
    
    int count;
    count = 0;
    
    while (count < 5) {
      int next;
      next = random.nextInt();
      
      if (next <= 0) {
        continue;
      }
      
      if ((next % 111_222_333) != 0) {
        continue;
      }
      
      String found;
      found = Integer.toString(next);
        
      queue.add(found);
      
      count++;
    }    
    
    server.interrupt();
    
    server.join();
    
    Logger.log("END");
  }
}

We create our web server task with an unbounded blocking queue. We run the task in a platform thread named "WEB".

Next, like our previous examples, we find positive multiples of 111_222_333 from random integer numbers. Whenever a multiple is found, its string representation is added to the queue. After 5 multiples have been found, we interrupt the server thread.

We wait for the server thread to terminate by invoking the join method.

Let's see it in action. Here's the output of one of the runs:

  main: BEGIN
   WEB: 556111665
   WEB: 1779557328
   WEB: 2113224327
   WEB: 1668334995
   WEB: 444889332
   WEB: Got interrupted!
  main: END

So, looking from outside, our current iteration behaves in the same way as our second iteration.

We did not explicitly check for the thread interrupted status using the Thread::interrupted method.

We did it indirectly by catching the InterruptedException instead.

The java.lang.InterruptedException exception

So a method which might block indefinitely may declare that it throws an InterruptedException. A method that does so is said to be interruptible.

Remember that a thread will only stop executing when it returns from the run method. By throwing an InterruptedException, a blocking method signals to the calling code that the current thread has been interrupted.

So, when you a method throws an InterruptedException, it does not necessarily mean that something went wrong.

It means that the executing thread, while blocked on a blocking method, got interrupted by another thread. Usually because it represents a task that got cancelled after it had been started.

Final thoughts

In this blog post we tried to answer the question "What is the InterruptedException in Java?"

The intention was to give an introduction to the Java developer.

We intentionally did not try to answer the question "How should I handle an InterruptedException in Java?"

Instead, it is best to understand why and when a method might throw the exception. This way, and with this knowledge, developers gain the autonomy to decide how best to handle InterruptedException within their code.

You can find the source code of the examples in this GitHub repository.