DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

How are you handling the data revolution? We want your take on what's real, what's hype, and what's next in the world of data engineering.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Mastering Concurrency: An In-Depth Guide to Java's ExecutorService
  • The Challenges and Pitfalls of Using Executors in Java
  • Coordinating Threads Using CountDownLatch
  • Java Thread Synchronization and Concurrency Part 2

Trending

  • Zero-Trust AI: Applying Cybersecurity Best Practices to AI Model Development
  • Stop Building Monolithic AI Brains, Build a Specialist Team Instead
  • Automating E2E Tests With MFA: Streamline Your Testing Workflow
  • Reinforcement Learning in CRM for Personalized Marketing
  1. DZone
  2. Coding
  3. Java
  4. InterruptedException and Interrupting Threads Explained

InterruptedException and Interrupting Threads Explained

Let's take a simple example of a thread that periodically does some clean up, but in between sleeps most of the time.

By 
Tomasz Nurkiewicz user avatar
Tomasz Nurkiewicz
DZone Core CORE ·
Jun. 01, 14 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
18.3K Views

Join the DZone community and get the full member experience.

Join For Free

If InterruptedException wasn't a checked exception, probably no one would even notice it - which would actually prevent couple of bugs throughout these years. But since it has to be handled, many handle it incorrectly or thoughtlessly. Let's take a simple example of a thread that periodically does some clean up, but in between sleeps most of the time.

class Cleaner implements Runnable {

  Cleaner() {
    final Thread cleanerThread = new Thread(this, "Cleaner");
    cleanerThread.start();
  }

  @Override
  public void run() {
    while(true) {
      cleanUp();
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }

  private void cleanUp() {
    //...
  }

}
This code is wrong on so many layers! 

  1. Starting Thread in a constructor might not be a good idea in some environments, e.g. some frameworks like Spring will create dynamic subclass to support method interception. We will end-up with two threads running from two instances.
  2. InterruptedException is swallowed, and the exception itself is not logged properly
  3. This class starts a new thread for every instance, it should use ScheduledThreadPoolExecutor instead, shared among many instances (more robust and memory-effective)
  4. Also with ScheduledThreadPoolExecutor we could avoid coding sleeping/working loop by ourselves, and also switch to fixed-rate as opposed to fixed-delay behaviour presented here.
  5. Last but not least there is no way to get rid of this thread, even when Cleaner instance is no longer referenced by anything else
All problems are valid, but swallowing InterruptedException is its biggest sin. Before we understand why, let us think for a while what does this exception mean and how we can take advantage of it to interrupt threads gracefully. Many blocking operations in JDK declare throwing InterruptedException, including:

  • Object.wait()
  • Thread.sleep()
  • Process.waitFor()
  • AsynchronousChannelGroup.awaitTermination()
  • Various blocking methods in java.util.concurrent.*, e.g. ExecutorService.awaitTermination(),Future.get(), BlockingQueue.take(), Semaphore.acquire() Condition.await() and many, many others
  • SwingUtilities.invokeAndWait()
Notice that blocking I/O does not throw InterruptedException (which is a shame). If all these classes declareInterruptedException, you might be wondering when is this exception ever thrown?

  • When a thread is blocked on some method declaring InterruptedException and you call Thread.interrupt()on such thread, most likely blocked method will immediately throw InterruptedException.
  • If you submitted a task to a thread pool (ExecutorService.submit()) and you call Future.cancel(true) while the task was being executed. In that case the thread pool will try to interrupt thread running such task for you, effectively interrupting your task.
Knowing what InterruptedException actually means, we are well equipped to handle it properly. If someone tries to interrupt our thread and we discovered it by catching InterruptedException, the most reasonable thing to do is letting said thread to finish, e.g.:

class Cleaner implements Runnable, AutoCloseable {

  private final Thread cleanerThread;

  Cleaner() {
    cleanerThread = new Thread(this, "Cleaner");
    cleanerThread.start();
  }

  @Override
  public void run() {
    try {
      while (true) {
        cleanUp();
        TimeUnit.SECONDS.sleep(1);
      }
    } catch (InterruptedException ignored) {
      log.debug("Interrupted, closing");
    }
  }

  //...   

  @Override
  public void close() {
    cleanerThread.interrupt();
  }
}
Notice that try-catch block now surrounds while loop. This way if sleep() throws InterruptedException, we will break out of the loop. You might argue that we should log InterruptedException's stack-trace. This depends on the situation, as in this case interrupting a thread is something we really expect, not a failure. But it's up to you. The bottom-line is that if sleep() is interrupted by another thread, we quickly escape from run() altogether. If you are very careful you might ask what happens if we interrupt thread while it's in cleanUp() method rather than sleeping? Often you'll come across manual flag like this:

private volatile boolean stop = false;

@Override
public void run() {
  while (!stop) {
    cleanUp();
    TimeUnit.SECONDS.sleep(1);
  }
}

@Override
public void close() {
  stop = true;
}
However notice that stop flag (it has to be volatile!) won't interrupt blocking operations, we have to wait until sleep()finishes. On the other side one might argue that explicit flag gives us better control since we can monitor its value at any time. It turns out thread interruption works the same way. If someone interrupted thread while it was doing non-blocking computation (e.g. inside cleanUp()) such computations aren't interrupted immediately. However thread is marked asinterrupted and every subsequent blocking operation (e.g. sleep()) will simply throw InterruptedExceptionimmediately - so we won't loose that signal. 

We can also take advantage of that fact if we write non-blocking thread that still wants to take advantage of thread interruption facility. Instead of relying on InterruptedException we simply have to check forThread.isInterrupted() periodically:

public void run() {
  while (Thread.currentThread().isInterrupted()) {
    someHeavyComputations();
  }
}
Above, if someone interrupts our thread, we will abandon computation as soon as someHeavyComputations() returns. If it runs for two long or infinitely, we will never discover interruption flag. Interestingly interrupted flag is not a one-time pad. We can call Thread.interrupted() instead of isInterrupted(), which will reset interrupted flag and we can continue. Occasionally you might want to ignore interrupted flag and continue running. In that case interrupted() might come in handy. BTW I (imprecisely) call "getters" that change the state of object being observed "Heisengetters".


Note on Thread.stop()

If you are old-school programmer, you may recall Thread.stop() method, which has been deprecated for 10 years now. In Java 8 there were plans to "de-implement it", but in 1.8u5 it's still there. Nevertheless, don't use it and refactor any code using Thread.stop() into Thread.interrupt(). 

Uninterruptibles from Guava

Rarely you might want to ignore InterruptedException altogether. In that case have a look at Uninterruptibles from Guava. It has plenty of utility methods like sleepUninterruptibly() or awaitUninterruptibly(CountDownLatch). Just be careful with them. I know they don't declare InterruptedException (which might be handful), but they also completely prevent current thread from being interrupted - which is quite unusual.

Summary

By now I hope you have some understanding why certain methods throw InterruptedException. The main takeaways are:

  • Caught InterruptedException should be handled properly - most of the time it means breaking out of the current task/loop/thread entirely
  • Swallowing InterruptedException is rarely a good idea
  • If thread was interrupted while it wasn't in a blocking call, use isInterrupted(). Also entering blocking method when thread was already interrupted should immediately throw InterruptedException
Blocking (computing) Thread pool Task (computing) Advantage (cryptography) IT Google Guava Java (programming language) Java Development Kit Blocks

Published at DZone with permission of Tomasz Nurkiewicz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Concurrency: An In-Depth Guide to Java's ExecutorService
  • The Challenges and Pitfalls of Using Executors in Java
  • Coordinating Threads Using CountDownLatch
  • Java Thread Synchronization and Concurrency Part 2

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: