Drought cavity be out of tune

Also like why, add a paragraph to each article that is out of line. That is to say, chat casually, maybe it is your recent life status or perception, some thoughts and thinking, maybe it is the original intention of writing this article and so on, or even just brag casually.

Maybe due to the pressure of work, I always feel not very happy. A few days ago, I even made fun of it on wechat moments. I was touched by the encouragement from many friends.

In fact, think about their own more than 20 years is also very lucky, family harmony, smooth feelings, school is also so-so, although the job jumped a few slots, but also smooth. I don’t have a car, a house, or kids, but I’m doing okay. I still have supermarket freedom. In fact, there is no need to complain too much, my parents’ generation is much harder than us, our so-called “hardship” is nothing at all.

Enter big factory also is own choice, diligently refuels.

This morning, I went to the West Lake for a walk. In the evening, I came back and had a big meal cooked by my family. Then I went outside and my friends taught me how to learn skateboarding, and I got a new skill. Come back at 10pm and continue writing until around 12:30pm (much later than expected), and you may actually be going bald. The more I write, the more I can’t help it…

That’s life. You can’t be happy unless you try to love it.

Come back to why I’m writing this article. In fact, WHEN I selected the topic on Friday, I struggled a little. I originally wanted to write Spring Ioc, which may have a wider audience. However, after a look, the source code is quite complicated, and I feel that it will take a lot of space to straighten it out. Every reader friend can also feel free to leave a message, you want to see which aspect of the article, give me some inspiration.

The idea of thread interruption is that some time ago in our multi-threaded ebook reader group, Pineapple asked a question about AQS, and then we briefly discussed it in the group, and came up with some preliminary answers.

Discussing thread interrupts

But when I came down, I wanted to thoroughly understand the mechanism of thread interruption. When I wrote a book on multithreading, I didn’t pay much attention to this part. There are not many articles about thread interrupts on the Internet. I hope this article can be helpful.

Usage scenarios for thread interrupts

Thread interrupt means we want to close a thread.

So when do thread interrupts need to be used? For example, when we take a didi, we can usually choose from multiple models. And if one car gets a call, it cancels all other rides.

Another example is that when we request a third-party API, we expect to get the result within a limited time. If we don’t, we want to cancel the task.

There are many similar examples. Whenever we need to interrupt something that another thread is doing, we can use thread interrupts to do it for us.

The thread interrupt mechanism is also widely used in Java’s various threading utility classes.

Preemptive and cooperative interrupts

Historically, Java has used the stop() method to terminate a thread. These are “preemptive interrupts.” But it caused a lot of problems and has long since been deprecated by the JDK. Using the stop() method can cause data inconsistencies and may not even stop the thread at all.

Interested students can watch the official this article: https://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

After a long time of development, Java finally chose to implement interrupts with a “collaborative” interrupt mechanism, which is now the implementation.

The so-called cooperative is achieved through an interrupt identification bit. If other threads want to interrupt thread A, they mark thread A’s interrupt flag bit. Thread A checks the interrupt flag bit by “polling” itself, and then does its own processing.

So the question is, where is this identifier bit? How do you poll? What does a thread polling for an interrupt do? These problems will be introduced in detail later, here is a simple explanation.

The thread interrupt identifier in Java is maintained by “native methods,” leaving only a few apis at the Java level for the user to fetch and manipulate.

How is polling implemented? Different scenarios have different implementations. Thread sleeping, waiting, and other scenarios are implemented through “polling by the JVM itself.” In some “Java concurrency tools,” polling is implemented at the “Java code level.”

What happens after a thread is interrupted? Different scenarios are implemented in different ways, often in conjunction with polling implementation. Generally, JVM polling implementations throw InterruptedException, while polling implementations at the Java code level do not throw exceptions.

Thread interrupt API

As mentioned earlier, thread interrupt bits in Java are maintained by native methods, but several apis are left for the user to manipulate. For Thread interrupts, the Thread class defines these methods:

// Interrupt the thread
public void interrupt(a) {}

// See if the thread has an interrupt flag set
public boolean isInterrupted(a) {}
 // Check whether the current thread has set the interrupt flag. This method will reset the interrupt flag, with side effects public static boolean interrupted(a) {} Copy the code

You can take a look at the JDK source code, these three methods are called at the bottom of the native method to implement, that is, we mentioned earlier, the interrupt flag bit is maintained by the local method, not at the Java code level.

There are two ways to check whether the current thread has been interrupted. The first is isInterrupted, which is simpler and has no side effects. The other is interrupted, which resets the interrupt flag and has side effects.

Without further ado, on the code:

Thread.currentThread().interrupt();
System.out.println(Thread.interrupted());
System.out.println(Thread.interrupted());
System.out.println(Thread.interrupted());

// Print the following: true false false Copy the code

And the two methods are different. One is the instance method, which checks whether a thread has set the interrupt flag. One is a static method that checks whether the current thread has an interrupt flag set. Different methods are used in different scenarios, especially interrupted. What is the meaning of this? We’ll look at some of their application scenarios in a selection of source code below.

Thread handling of interrupts

Threads handle interrupts differently in different states. Java threads have six states, defined in the class Thread.State.

/ / Thread. State the source code
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
 WAITING,  TIMED_WAITING,  TERMINATED; } Copy the code

This article will not go into details about the six states of Java and their conversion relations. Readers who have questions can reply “Multithreading” on the public account, and they can get our multithreading ebook, which is introduced in detail in Chapter 4.

NEW/TERMINATE

If the thread is TERMINATED or in NEW state, calling interrupt() has no effect on it, and the interrupt flag is not set.

RUNNABLE/BLOCKED

If a thread is in a RUNNABLE (running) or BLOCKED (awaiting a lock) state, calling interrupt() sets the interrupt flag but does not affect the thread’s state.

WAITING/TIMED_WAITING

The thread is in a WAITING or TIMED_WAITING state, which is called “blocked” for convenience. In a blocked state, the JVM actively polls for interrupt flag bits.

There are generally two ways to enter the blocking state. The first way is to use sleep, wait, join (the basic implementation of join is to use wait), and so on. Both apis explicitly throw InterruptedException:

/ / into the WAITING
public final void join(a) throws InterruptedException;
public final void wait(a) throws InterruptedException;

/ / enter TIMED_WAITING
public final native void wait(long timeout) throws InterruptedException; public static native void sleep(long millis) throws InterruptedException; public final synchronized void join(long millis) throws InterruptedException; Copy the code

If calling interrupt() causes a thread to block, it “wakes the thread up and throws InterruptedException.”

Note that after an exception is thrown, the interrupt flag bit is cleared because the thread’s interrupt flag bit is reset from true to false because the thread is back in the “ready” state to handle the exception.

In addition to the above methods, another way to block a thread is the locksupport. park method that is widely used in Java multithreaded utility classes.

Unlike the sleep, wait, and Join methods, however, the Park method “immediately returns no more blocking but does not throw an exception” when the thread interrupt flag is set to true.

IO Handling of interrupts

This is actually commented in more detail in the source code for the Interrupt method. Actually only support for NIO.

The BIO does not support interrupts, such as InputStream. The interrupt thread will set the identity bit, but the IO will not handle the interrupt.

If the current thread is blocked by an IO operation and the Channel implements the InterruptibleChannel interface (all major NIO channels implement this interface), the current thread’s interrupt bit is set to true and the Channel is shut down. Thread will then receive an ClosedByInterruptException exception (the exception is defined in the NIO package).

If the current thread is blocked by a Selector, the interrupt bit of the current thread is set to true, and the Selector immediately returns a non-zero number.

Source code analysis

So how does the JDK use thread interrupts? Let’s pick two familiar multithreaded tool classes to talk about.

AQS

First is the famous AQS, as the foundation of many Java multithreaded tool classes, in the Java multithreaded river’s lake in a respected position. The core method acquire is the most important. The acquireQueued method is called at the bottom of the Acquire method, and it’s the code here that inspired me to write this article.

As for AQS, this article will not go into details. If you have any questions, you can reply “Multithreading” on the official account, and you can get our multithreading ebook, which is explained in detail in Chapter 11.

First to appreciate this section of the ball do not understand the source:

// access the resource
public final void acquire(int arg) {
    if(! tryAcquire(arg) &&        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}  // Interrupt yourself static void selfInterrupt(a) {  Thread.currentThread().interrupt(); }  // Queue for resources final boolean acquireQueued(final Node node, int arg) {  boolean interrupted = false;  try {  for (;;) {  final Node p = node.predecessor();  if (p == head && tryAcquire(arg)) {  setHead(node);  p.next = null; // help GC  return interrupted;  }  if (shouldParkAfterFailedAcquire(p, node))  interrupted |= parkAndCheckInterrupt();  }  } catch (Throwable t) {  cancelAcquire(node);  if (interrupted)  selfInterrupt();  throw t;  } }  // park and check for interruption private final boolean parkAndCheckInterrupt(a) {  LockSupport.park(this);  return Thread.interrupted(); } Copy the code

PS: This is the source code for Java 11, with some minor changes based on the source code for Java 8. | = meaning and + = – = similar, you know.

Look at the acquireQueued section of the code and decide if you should park the lock if it fails. If so, go to park, enter the block, and check for interrupts. If interrupted, set this to true and return above, and the acquire method above calls selfInterrupt to set the interrupt flag bit for the current thread.

Let’s consider a few questions here:

What happens after the interruption?

After interruption, the code sets the value of interrupted to true, but the next loop continues

  • If the resource is obtained this time, it is returned to the above, to the current thread set interrupt flag bit;
  • If no resource is retrieved, the park thread continues

Why not return immediately after an interrupt instead of continuing the loop?

The current thread is set to interrupt by another thread when it fails to retrieve the resource, but that does not mean that the thread should immediately stop all work and return. The so-called “cooperative interrupt” means that I should choose a place where I feel safe to interrupt.

For this method, the loop continues trying to get the resource until the current thread completes the task and hands it off to the outside world.

To put it simply, I don’t care if there is no interruption, I just finish my own work, and you deal with the interruption yourself.

Why is the interrupt flag bit not set to true when the resource is obtained?

Since parkAndCheckInterrupt called Thread.interrupted, the emphasis bit has been reset to false, and “the message that the current Thread was interrupted has been lost”, so this message must be “compensated” to tell the Thread that it was interrupted.

Why does the acquire method selfInterrupt?

It is still “compensation interrupt information”. Here is the real compensation, reset the interrupt bit of the current thread to true. Since the current thread was actually interrupted, I don’t have to worry about it myself, but I need to set the interrupt bit back so that the outside world knows about it.

Why use interrupted instead of isInterrupted here?

Because parkAndCheckInterrupt is in a loop. As mentioned above, if the resource is not available this time, the loop will continue. So what if you still can’t get it? The current thread should be park again. If isInterrupted is used, the second time Park sees that the interrupt bit of the current thread has been set to true, the current thread will not block and will go back. This is not true.

Here’s a simple test:

Thread thread = new Thread(() -> {
    LockSupport.park();
    System.out.println("first parked");
    Thread.currentThread().isInterrupted();
    LockSupport.park(); // There is no blocking because the interrupt bit is true
 System.out.println("second parked");  Thread.interrupted();  LockSupport.park(); // This is blocked because the interrupt bit is false  System.out.println("third parked"); }); thread.start(); thread.interrupt(); // Interrupt to wake up park for the first time  // Print out: // first parked // third parked Copy the code

Lock

The Lock interface has a method:

void lockInterruptibly(a) throws InterruptedException;
Copy the code

Its implementation is essentially calls AQS acquireSharedInterruptibly method, also have a subclass is own implementation, but how about the same. The current Thread is checked for interruption with thread.interrupted () and the identity bit is reset. If it has, InterruptedException is thrown directly.

And we also know, Lock interface Lock method, is also basically using AQS to achieve, but each acquire 1 resources.

So the Lock interface provides two kinds of locks to handle interrupts. One is the Lock method, which does nothing with the interrupt. The other is the lockInterruptibly method, which throws an exception when interrupted. Specific use of which way, by the user to choose, very flexible.

ThreadPoolExecutor

There it goes again, thread pools!

As you know, the thread pool maintains a Worker queue, which is our thread being reused. The runWorker method uses this queue to continually fetch and run tasks. There’s a piece of code in there, and I’m afraid we don’t understand it, and I wrote a nice comment:

// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
 (Thread.interrupted() &&  runStateAtLeast(ctl.get(), STOP))) && ! wt.isInterrupted()) wt.interrupt(); Copy the code

In simple terms, if the thread pool is closing, make sure the thread is interrupted; If the thread pool is not closed, make sure the thread is not interrupted.

Take a look at the thread pool shutdown method, which calls interruptIdleWorkers to interrupt all idle workers. The shutdownNow method, on the other hand, calls interruptWorkers and is used to interrupt all workers, whether you are idle or not.

So if we want to gracefully shutdown the thread pool, it’s better to call shutdown.

conclusion

After such a large analysis, we can draw some conclusions:

  • Java uses cooperative interrupts where thread interrupts simply set the identifier bit
  • Different thread states handle interrupts differently, among which blocking state is the most complicated
  • Sleep, wait, join, and so on are instances where the JVM polls for identity bits and throws exceptions
  • Park is the POLLING identifier for the JVM, but does not throw exceptions and leaves it up to the user to handle interrupts
  • Nio can respond to interrupts
  • Use Lock Lock is better, you can freely choose not to throw interrupt exceptions

So, would you use a thread interrupt off?

About the author

I’m Yasin, a good-looking and interesting programmer.

Wechat public number: made up a process

Personal website: https://yasinshaw.com

Pay attention to my public number, grow up with me ~

The public,

This article is formatted using MDNICE