The java.lang.InterruptedException: an introduction to the Java developer
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:
-
tries to find the current executing thread; and
-
prints the thread name along with the specified log message.
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.