Handling InterruptedException

The risk of InterruptedException appears very often in multi-threading environment. As a declared exception, it must be defined either in method signature or caught in try-catch block. However, it must be done in some correct way.

Looking for a better data engineering position and skills?

You have been working as a data engineer but feel stuck? You don't have any new challenges and are still writing the same jobs all over again? You have now different options. You can try to look for a new job, now or later, or learn from the others! "Become a Better Data Engineer" initiative is one of these places where you can find online learning resources where the theory meets the practice. They will help you prepare maybe for the next job, or at least, improve your current skillset without looking for something else.

👉 I'm interested in improving my data engineering skillset

See you there, Bartosz

What is InterruptedException ?

Internally each thread has a flag called interrupt status, telling if it was interrupted. Throwing an InterruptedException means that some other process interrupted occupied (sleeping, waiting...) thread. The problem about this exception is that every time when it's thrown, it resets interrupt status to false. In consequence, any thread from higher level can't know about thread interruption.

This lack of knowledge can lead to problematic situations. For instance, when ThreadPoolExecutor launches thread workers, it checks every time if started worker is not interrupted. The verification is made on Thread's isInterrupted() and interrupted() methods. The first one is an instance method and returns true if given Thread has been interrupted. The second one is class static method and it's odder than isInterrupted(). Its oddity consists on the fact that 2 subsequent calls won't return the same result ! The first call will return true or false, depending if current thread was interrupted. But the second call will return false because every time interrupt status is reset.

Are you observed something else ? Yes, there are 2 methods to check if given thread has been interrupted and thus, 2 types of thread: current thread and currently executing thread. What's the difference between them ? The current thread is a thread represented by given instance of Thread class. The currently executing thread is a thread being executed in given context. You can see the difference in the 3rd section on test called should_see_the_difference_between_thread_and_currently_executing_thread.

Handle InterruptedException

That's all for parenthesis and let's go to the solution for interrupt status reset. The solution consists on implementing auto-management for InterruptedException in potentially interruptible thread. Here we can find some good and bad practices to deal with InterruptedException.

First of all, let's see what shouldn't be done:

Instead, some of good practices are advised:

InterruptedException in tests

Here we can see some examples illustrating the differences between interrupt-related methods and 2 types of threads we've seen in the first section:

@Test
public void should_prove_that_interrupted_resets_thread_status() throws InterruptedException {
  Thread longRunningTask = new Thread(() -> {}, "test");
  longRunningTask.start();
  // interrupted() refers to current thread while interrupt(), as na instance
  // method, refers to Thread object that it is called on.
  // It's why here we call currentThread().interrupt()
  Thread.currentThread().interrupt();

  assertThat(Thread.currentThread().interrupted()).isTrue();
  // At the 2nd call, interrupted flag should be reset
  assertThat(Thread.currentThread().interrupted()).isFalse();
  // The instance isInterrupted() method should be false
  assertThat(longRunningTask.isInterrupted()).isFalse();
}

@Test
public void should_have_inconsistent_interrupted_status_for_badly_handled_interrupted_exception() throws InterruptedException {
  CountDownLatch latch = new CountDownLatch(1);
  boolean[] interruptedThreadStatus = new boolean[] {false};
  Thread longRunningTask = new Thread(() -> {
    try {
      Thread.sleep(5_000L);
    } catch (InterruptedException e) {
      // Do nothing
    }
    interruptedThreadStatus[0] = Thread.currentThread().isInterrupted();
    latch.countDown();
  });
  longRunningTask.start();
  longRunningTask.interrupt();
  latch.await(2, TimeUnit.SECONDS);

  assertThat(interruptedThreadStatus[0]).isFalse();
}

@Test
public void should_have_consistent_interrupted_status_for_correctly_2() throws InterruptedException {
  CountDownLatch latch = new CountDownLatch(1);
  boolean[] interruptedThreadStatus = new boolean[] {false};
  Thread longRunningTask = new Thread(() -> {
    try {
      Thread.sleep(5_000L);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    interruptedThreadStatus[0] = Thread.currentThread().isInterrupted();
    latch.countDown();
  });
  longRunningTask.start();
  longRunningTask.interrupt();
  latch.await(2, TimeUnit.SECONDS);

  assertThat(interruptedThreadStatus[0]).isTrue();
}

@Test
public void should_see_the_difference_between_thread_and_currently_executing_thread() throws InterruptedException {
  Map<String, String> nameContexts = new HashMap<>();
  CountDownLatch latch = new CountDownLatch(2);
  Thread longRunningTask1 = new Thread(() -> {
    Uninterruptibles.sleepUninterruptibly(5L, TimeUnit.SECONDS);
    nameContexts.put("T1-current", Thread.currentThread().getName());
    Thread t = new Thread(() -> {
      nameContexts.put("T1_child-current", Thread.currentThread().getName());
    }, "T1_child");
    t.start();
    latch.countDown();
  }, "T1");
  longRunningTask1.start();

  latch.await(6L, TimeUnit.SECONDS);

  assertThat(nameContexts.get("T1-current")).isEqualTo("T1");
  assertThat(nameContexts.get("T1_child-current")).isEqualTo("T1_child");
  // currently executing thread from this point of view will be
  // probably called 'main' but we'll only check if it's not one of previously defined
  assertThat(Thread.currentThread().getName()).isNotEqualTo("T1");
  assertThat(Thread.currentThread().getName()).isNotEqualTo("T1_child");
  assertThat(longRunningTask1.getName()).isEqualTo("T1");
}

This post describes some aspects of interruptible world in Java. In the first section it introduces InterruptedException, produces when blocking event (sleep, wait...) is interrupted. It also explains why this exception should be handled differently than the others and what is the difference between current thread and currently executing thread. The second part shows bad and good practices to deal with InterruptedException. At the end some tests shows interrupt-related methods.