跳至主要内容

Interrupt Thread

Recently, I am handling the graceful shutdown of Syncer. In the process of implementation, I was surprised by some counterintuitive part of Java. Let’s go on and see whether you know it.

Thread States

The following enumerations are states of Java Thread which is cited from Java documentation:

  • NEW
    A thread that has not yet started is in this state.
  • RUNNABLE
    A thread executing in the Java virtual machine is in this state.
  • BLOCKED
    A thread that is blocked waiting for a monitor lock is in this state.
  • WAITING
    A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
  • TIMED_WAITING
    A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
  • TERMINATED
    A thread that has exited is in this state.

The naming of state is fine, but those description is not clear enough though:

  • What a Thread's state if it blocked in IO operation?
  • What a Thread's state if it called sleep, wait?
  • What a Thread's state if it is waiting to enter Synchronized method/block?
  • What a Thread's state if it is waiting to gain a Lock?

In order to understand the differences between them better, we use run some examples to see:

public static void main(String[] args) throws InterruptedException {  
  test(States::getSleep);  
  test(States::getWait);  
  test(States::getIO);  
  // ...
}  
  
private static void test(Supplier<Runnable> supplier) throws InterruptedException {  
  System.out.println("-------");  
  Thread thread = new Thread(supplier.get());  
  System.out.println(thread.getState());  
  thread.start();  
  Thread.sleep(1000);  
  System.out.println(thread.getState());  
  Thread.sleep(1000);  
  System.out.println(thread.getState());  
  thread.interrupt();  
  System.out.println(thread.getState());  
  System.out.println("-------");  
}  
  
private static Runnable getSleep() {  
  return () -> {  
    try {  
      Thread.sleep(2000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
  };  
}  
private static Runnable getIO() {  }  
private static Runnable getWait() {  }

From the output, we can answer above answer:

  • A thread blocking in IO is RUNNABLE;
  • A thread in sleep() is TIMED_WAITING;
  • A thread n wait() is WAITING, in wait(long timeout) is TIMED_WAITING;
  • A thread waiting for Synchronized is BLOCKED;
  • A thread waiting for lock is WAITING, in lockInterruptibly is TIMED_WAITING;

If you have doubt about the state of thread in other state, just write some examples to verify you guess. oow o Interrupt

Understand the state of states, we are going to see how to interrupt them when they are in different states. If thread in NEW or TERMINATED, we don’t need to interrupt them, so we only focus on th oher s.

The interrupt mechanism in Java is just a flag in thread object, so if thread is RUNNABLE and doesn’t check thread status, it will just continue to run. For example, if we have a while loop to do some calculation and not check interrupt flag, it will just continue; If our code block in IO, interrupt may be work. There is an exception called java.io.InterruptedIOException, which is supposed to cover the situation where you interrupt a thread that is waiting o s I octe However, the reality is that it is only implemented by a few IO operation in a few class like PipedReader.

Threads can be WAITING/TIMED_WAITING on wait() s,tanlb oth thr a nterruptctio when the flag of thread is set.

We ot ut a thread in otr a ynhronized if we e o ordead obe e to twn otdsryst leay. But if we called Lock#lockInterruptibly, we can interrupt it.

In order to set the interrupt flag of thread, we can:

  • call Thread#interrup();
  • call Future#cancel(true)
  • shutdownNow(..) method on your ExecutorService

Shutdown ShutdownNow

The first thing make us surprised is the differences between Shutdown and ShutdownNow:

  • shutdown() will just tell the executor service that it can’t accept new tasks, but the already submitted tasks continue to run
  • shutdownNow() will do the same AND will try to cancel the already submitted tasks by interrupting the relevant threads. Note that if our tasks ignore the interruption, shutdownNow will behave exactly the same way as shutdown.

Ctrl-C?

Another counter-intuitive things is JVM will not send signal from OS to Java's thread, i.e. JVM will just shutdown and not set interrupt flag for threads or throw InterruptException.

signal 2: SIGINT
signal 9: SIGKILL

Even though JVM do nothing for Ctrl-C, it has a Ctrl-Break handler to do thread dump.

How to Handle Interrupt

For a clean shutdown of thread, we should not swallow InterruptedException:

try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  Thread.currentThread().interrupt(); // re-interrupt or re-throw
  throw new RuntimeException(ex);
}

or loop with thread state

while (!Thread.interrupted()) {  
  // event handle
}

Ref

Written with StackEdit.

评论

此博客中的热门博文

Spring Boot: Customize Environment

Spring Boot: Customize Environment Environment variable is a very commonly used feature in daily programming: used in init script used in startup configuration used by logging etc In Spring Boot, all environment variables are a part of properties in Spring context and managed by Environment abstraction. Because Spring Boot can handle the parse of configuration files, when we want to implement a project which uses yml file as a separate config file, we choose the Spring Boot. The following is the problems we met when we implementing the parse of yml file and it is recorded for future reader. Bind to Class Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties. As the document says, there exists three ways to access properties in *.properties or *.yml : @Value : access single value Environment : can access multi

Elasticsearch: Join and SubQuery

Elasticsearch: Join and SubQuery Tony was bothered by the recent change of search engine requirement: they want the functionality of SQL-like join in Elasticsearch! “They are crazy! How can they think like that. Didn’t they understand that Elasticsearch is kind-of NoSQL 1 in which every index should be independent and self-contained? In this way, every index can work independently and scale as they like without considering other indexes, so the performance can boost. Following this design principle, Elasticsearch has little related supports.” Tony thought, after listening their requirements. Leader notice tony’s unwillingness and said, “Maybe it is hard to do, but the requirement is reasonable. We need to search person by his friends, didn’t we? What’s more, the harder to implement, the more you can learn from it, right?” Tony thought leader’s word does make sense so he set out to do the related implementations Application-Side Join “The first implementation

Implement isdigit

It is seems very easy to implement c library function isdigit , but for a library code, performance is very important. So we will try to implement it and make it faster. Function So, first we make it right. int isdigit ( char c) { return c >= '0' && c <= '9' ; } Improvements One – Macro When it comes to performance for c code, macro can always be tried. #define isdigit (c) c >= '0' && c <= '9' Two – Table Upper version use two comparison and one logical operation, but we can do better with more space: # define isdigit(c) table[c] This works and faster, but somewhat wasteful. We need only one bit to represent true or false, but we use a int. So what to do? There are many similar functions like isalpha(), isupper ... in c header file, so we can combine them into one int and get result by table[c]&SOME_BIT , which is what source do. Source code of ctype.h : # define _ISbit(bit) (1 << (