Blog |

How to Handle the InterruptedException Checked Exception in Java

How to Handle the InterruptedException Checked Exception in Java
Table of Contents

Introduction: Java Threads

Threads are a basic concept in concurrent and parallel programming [1]. They allow programs to do multiple things at the same time and are often used for performing computationally intensive tasks in the background without interrupting the main program. This is accomplished through multithreading, a common programming and execution model which allows multiple threads to exist within the context of one process. These threads share resources but are able to execute independently.

In Java, threads are lightweight [2], which means they run in the same memory context and the time it takes to switch between threads is very short. This also means that inter-thread communication is fast and simple. Each and every Java application has a special thread created by the Java Virtual Machine, called the main thread, which is initiated by the main() method as the application’s entry point. From there, it is possible to have many user threads created and controlled from within the application, able to execute portions of the program code in parallel with the main thread.

The conventional way to create a thread in Java is by utilizing the Thread class [3], often used in conjunction with the Runnable interface [4]. Threads can be started, stopped, suspended, or otherwise switched between their life-cycle states [5]. In addition, Java threads can also be interrupted.

Java Thread Classes

 

InterruptedException Exception: What, Why & How?

InterruptedException (from java.lang.InterruptedException) is a checked exception [6] which directly extends java.lang.Exception. This exception is thrown when a thread is waiting, sleeping, or otherwise occupied, and the thread is interrupted, either before or during the activity [7]. If an InterruptedException is caught it means that the Thread.interrupt() method is called by some code, somewhere, on the currently running thread of the program. As a matter of fact, the only thing that can interrupt a thread is a call to Thread.interrupt() [8].

Behind the scenes, Java’s interrupt mechanism is implemented with an internal flag denoting a thread’s interrupt status. Invoking Thread.interrupt() sets this flag. To check the interrupt status of a specific thread, the Thread.isInterrupted() instance method can be used. The static method Thread.interrupted() can be invoked to observe and clear the interruption status of the current thread; the thread can then be interrupted again at any point, including immediately after clearing/unsetting its interrupt status.

By convention, any method that exits by throwing an InterruptedException clears the interrupt status of the calling thread. And it is always possible to set the interrupt status afterwards, by another thread invoking Thread.interrupt().

 

How to handle the InterruptedException Exception

For simple, single-threaded programs, no special handling of this exception is necessary, so long as the code is never calling Thread.interrupt() either directly, or indirectly (through another method). However, understanding the InterruptedException exception is important, as handling it incorrectly may lead to code that’s difficult to manage and behaves poorly when executed in concurrent environments.

When calling a method that throws an InterruptedException, the main question to ask is whether it is possible and sensible to (re)throw the InterruptedException exception or not. The answer to this question then dictates what ought to be done.

 

Scenario #1: Throwing InterruptedException

It is often very difficult to ascertain that a running thread will never be interrupted. This is especially true when writing code that depends on external resources (e.g. network calls to external APIs) or is meant to be executed by some thread-management mechanism such as an Executor [9]. In this type of scenario, it is critical that the code responds promptly and cleanly to interrupts, so as to avoid an application from stalling or spurring a deadlock.

The best solution here is to let the InterruptedException propagate through the method call stack, by appending each relevant method signature with the throws InterruptedException statement. This might seem like an easy cop out solution at first glance, but it is, in fact, the correct solution.

// let the caller determine how to handle the interrupt
public double longRunningMethod() throws InterruptedException {
  // any InterruptedException thrown below will be propagated
  double x = someExternalAPI.calculateValueOfX();
  double y = anotherExternalAPI.calculateValueOfY();
  return x * y;
}

 

Scenario #2: Catching InterruptedException (the correct way)

There are situations where handling the exception with a try-catch block is the right way to go. This usually holds when either of the following is true:

  • it is known in advance that the InterruptedException exception will not, and therefore should not, be encountered; or
  • there is a possibility of the InterruptedException exception occurring, but it makes more sense for the method to resolve it in a self-contained manner, without propagating it up the call stack.

In either case, it is important not to ignore or disguise the exception, by doing something like this:

// bad approach 
try {
  TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignored) {}

Or the following:

// ill-advised
try {
  TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
  throw new RuntimeException(e);
}

The above approaches should be avoided as they only suppress the exception and mask the underlying issue.

 

Scenario #2.1 (Catch & Crash)

If a program is expected to never throw the InterruptedException exception, such an event should be treated as a serious violation of the program’s basic assumptions, and the appropriate way to handle this scenario would look something like this:

// acceptable solution
try {
  TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();  // reset interrupt status
  throw new AssertionError(e);         // crash with a runtime error
}

The above snippet accomplishes two things. First it restores the interrupt status of the thread by invoking the Thread.currentThread().interrupt() method and allowing the caller to detect the interrupt if it needs to. Then it wraps the InterruptedException in an AssertionError, which it throws to indicate that the basic invariants of the program had been violated. This approach addresses the interrupt in a manner considerate to both the called and the calling method, by logging the fact that an interrupt has occurred and crashing the program, asserting it ran into a serious problem.

 

Scenario #2.2 (Catch & Proceed)

In limited cases, such as when overriding a method that doesn't throw any checked exceptions or implementing the Runnable interface, it is sensible to account for the possibility of the InterruptedException exception being thrown, but let the program move forward without crashing. In this type of scenario, the interrupt status can be reset without raising an exception, with the expectation that the code being executed next is going to handle the interrupt. This effectively delays handling the interrupt, but it doesn’t ignore or suppress the event entirely.

// reset interrupt state and proceed without crashing 
public Optional<Double> getOptionalResult() {
    try {
        double result = longRunningMethod();
        return Optional.of(result);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return Optional.empty();
    }
}

 

Summary

Threads are an important construct in multi-threaded environments. Being a multithreaded language, Java provides different mechanisms for working with threads. One of these mechanisms is Java’s built-in thread interrupt system, whose main purpose is to provide a common, well-defined framework for allowing threads to interrupt long-running tasks in other threads in a clean and consistent manner. One key component of this system is the InterruptedException checked exception, used by Java programs to recognize and handle thread interruptions. This article explains the basic mechanism behind Java’s interrupt system and the ways in which the InterruptedException exception should and shouldn't be handled.

 

Track, Analyze and Manage Errors With Rollbar

Rollbar in action

Managing Java errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Sign Up Today!

 

References

[1] Wikipedia.org, 2021. List of concurrent and parallel programming languages - Wikipedia. Wikimedia Foundation, Inc. [Online]. Available: https://en.wikipedia.org/wiki/List_of_concurrent_and_parallel_programming_languages
. [Accessed Jan. 15, 2022]

[2] Techopedia.com, 2022. What is a Lightweight Thread? - Definition from Techopedia. [Online]. Available: https://www.techopedia.com/definition/24784/lightweight-thread. [Accessed Jan. 15, 2022]

[3] Oracle.com, 2021. Thread (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html. [Accessed Jan. 15, 2022]

[4] Oracle.com, 2021. Runnable (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Runnable.html. [Accessed Jan. 15, 2022]

[5] R. Perera, 2021. States of Thread in Java. Medium. [Online]. Available: https://levelup.gitconnected.com/states-of-thread-in-java-6f7bc1c628b9. [Accessed Jan. 15, 2022]

[6] Rollbar, 2022. How to Handle the Exception In Initializer Runtime Error in Java. Rollbar Editorial Team. [Online]. Available: https://rollbar.com/blog/java-exceptionininitializererror-runtime-error/. [Accessed Jan. 15, 2022]

[7] Oracle, 2021. InterruptedException (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/InterruptedException.html. [Accessed Jan. 15, 2022]

[8] Oracle, 2021. The Java® Language Specification, Chapter 17. Threads and Locks, Interruptions. Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/specs/jls/se17/html/jls-17.html#jls-17.2.3. [Accessed Jan. 15, 2022]

[9] Oracle, 2021. Executor (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Executor.html. [Accessed Jan. 15, 2022]

Related Resources

"Rollbar allows us to go from alerting to impact analysis and resolution in a matter of minutes. Without it we would be flying blind."

Error Monitoring

Start continuously improving your code today.

Get Started Shape