The interviewer asked: how to interrupt a thread and how to implement it?

Posted by Pino on Wed, 09 Feb 2022 15:58:02 +0100

Interrupt thread

Thread of thread Interrupt () method is to interrupt a thread. It will set the interrupt status bit of the thread, that is, set it to true. Whether the thread dies as a result of the interrupt, waits for a new task, or continues to run to the next step depends on the program itself. The thread will detect this interrupt flag bit from time to time to determine whether the thread should be interrupted (whether the interrupt flag value is true). It does not interrupt a running thread like the stop method.

Determine whether the thread is interrupted

To determine whether a thread has sent an interrupt request, use thread currentThread(). Isinterrupted() method (because it will not immediately clear the interrupt flag bit after setting the thread interrupt flag bit to true, that is, it will not set the interrupt flag to false), instead of using thread Interrupted() (this method will clear the interrupt flag bit after calling, i.e. reset it to false). The following is the interrupt mode when the thread is in the loop:

while(!Thread.currentThread().isInterrupted() && more work to do){
    do more work
}

How to interrupt a thread

If the thread is in the state of i.thread and o.wait, which can be blocked after calling the interrupt. Thread. 5 method, and if the thread is in the state of o.thread and o.wait, it can be blocked after calling the interrupt, The InterruptedException exception will be thrown at the calls of these blocking methods (sleep, join, wait, condition.await in 1.5 and I/O operation methods on Interruptible channels), and the interrupt flag bit of the thread will be cleared immediately after the exception is thrown, that is, reset to false. The purpose of throwing an exception is to wake up the thread from the blocking state and give the programmer enough time to process the interrupt request before ending the thread.

Note: synchronized cannot be interrupted during lock acquisition, which means that if a deadlock occurs, it cannot be interrupted (please refer to the following test examples). Reentrantlock, similar to synchronized function The same is true for the lock () method, which is also non interruptible, that is, if a deadlock occurs, reentrantlock The lock () method cannot be terminated. If it is blocked when called, it blocks until it obtains the lock. But if you call the tryLock method with timeout, reentrantlock tryLock (long timeout, timeunit unit). If the thread is interrupted while waiting, an InterruptedException exception will be thrown. This is a very useful feature because it allows the program to break the deadlock. You can also call reentrantlock Lockinterruptibly() method, which is equivalent to a tryLock method with infinite timeout.

Without any language requirements, an interrupted thread should be terminated. Interrupting a thread is just to attract the attention of the thread. The interrupted thread can decide how to deal with the interrupt. Some threads are so important that they should ignore the interrupt and continue to execute after handling the thrown exception. However, more generally, a thread will regard the interrupt as a termination request. The run method of this thread follows the following form:

public void run() {
    try {
        ...
        /*
         * No matter whether thread blocking methods such as sleep, join and wait have been called in the loop, they still need to be added here
         * !Thread.currentThread().isInterrupted()Condition, although the loop exits after throwing an exception, the
         * It is unnecessary to use blocking, but it will be safer and more timely if the blocking method is called but there is no blocking.
         */
        while (!Thread.currentThread().isInterrupted()&& more work to do) {
            do more work
        }
    } catch (InterruptedException e) {
        //The thread was interrupted during a wait or sleep
    } finally {
        //Do some cleanup before the thread ends
    }
}

The above shows that the while loop is in the try block. If the try is in the while loop, the interrupt flag should be reset in the catch block, because the interrupt flag bit will be cleared automatically after throwing the InterruptedException exception. At this time, it should be as follows:

public void run() {
    while (!Thread.currentThread().isInterrupted()&& more work to do) {
        try {
            ...
            sleep(delay);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();//Reset interrupt flag
        }
    }
}

Bottom interrupt exception handling method

In addition, do not catch the InterruptedException exception in your underlying code and do not handle it. It will be handled improperly, as follows:

void mySubTask(){
    ...
    try{
        sleep(delay);
    }catch(InterruptedException e){}//Don't do that
    ...
}

If you don't know how to handle the exception after throwing InterruptedException, you have the following good suggestions:

1. In the catch clause, call Thread.. currentThread. Interrupt() to set the interrupt status (because the interrupt flag will be cleared after an exception is thrown), so that the outside world can judge thread currentThread(). Isinterrupted() flag to decide whether to terminate the thread or continue. This should be done:

void mySubTask() {
    ...
    try {
        sleep(delay);
    } catch (InterruptedException e) {
        Thread.currentThread().isInterrupted();
    }
    ...
}

2. Or, it's better not to use try to catch such an exception and let the method throw it directly:

void mySubTask() throws InterruptedException {
    ...
    sleep(delay);
    ...
}

Interrupt application

Use interrupt semaphores to interrupt non blocking threads

The best and most recommended way to interrupt a thread is to use shared variable s to signal that the thread must stop the running task. The thread must periodically check this variable and then abort the task in an orderly manner. Example2 describes this approach:

class Example2 extends Thread {
    volatile boolean stop = false;// Thread interrupt semaphore

    public static void main(String args[]) throws Exception {
        Example2 thread = new Example2();
        System.out.println("Starting thread...");
        thread.start();
        Thread.sleep(3000);
        System.out.println("Asking thread to stop...");
        // Set interrupt semaphore
        thread.stop = true;
        Thread.sleep(3000);
        System.out.println("Stopping application...");
    }

    public void run() {
        // Detect the interrupt semaphore every second
        while (!stop) {
            System.out.println("Thread is running...");
            long time = System.currentTimeMillis();
            /*
             * Do not use the sleep method when simulating the loop, otherwise the sleep method will be used here
             * InterruptedException Exit the loop due to an exception, so that the stop condition detected by while will not be executed,
             * Lost its meaning.
             */
            while ((System.currentTimeMillis() - time < 1000)) {}
        }
        System.out.println("Thread exiting under request...");
    }
}

Use thread Interrupt() interrupts a non blocking thread

Although Example2 this method requires some coding, it is not difficult to implement. At the same time, it gives threads the opportunity to do the necessary cleanup. It should be noted here that the shared variable needs to be defined as volatile type or all access to it should be enclosed in synchronized blocks/methods. The above is a common way to interrupt a thread in a non blocking state, but it is more concise to detect the isInterrupted() condition:

class Example2 extends Thread {
    public static void main(String args[]) throws Exception {
        Example2 thread = new Example2();
        System.out.println("Starting thread...");
        thread.start();
        Thread.sleep(3000);
        System.out.println("Asking thread to stop...");
        // Issue interrupt request
        thread.interrupt();
        Thread.sleep(3000);
        System.out.println("Stopping application...");
    }

    public void run() {
        // Check whether the interrupt flag is set every second
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("Thread is running...");
            long time = System.currentTimeMillis();
            // Simulate sleep using a while loop
            while ((System.currentTimeMillis() - time < 1000) ) {
            }
        }
        System.out.println("Thread exiting under request...");
    }
}

So far, everything is going well! But what happens when a thread is blocked waiting for something to happen? Of course, if a thread is blocked, it cannot verify shared variables and cannot stop. This happens in many cases, such as calling object wait(),ServerSocket.accept() and datagram socket When receiving (), here are just some examples.

They may permanently block threads. Even if a timeout occurs, it is not feasible and appropriate to wait until the timeout period expires. Therefore, some mechanism should be used to make the thread exit the blocked state earlier. Let's take a look at the interrupt blocking thread technology.

Use thread Interrupt() interrupts threads in blocking state

Thread. The interrupt () method does not interrupt a running thread. What this method actually does is to set the interrupt flag bit of the thread, throw an exception InterruptedException where the thread is blocked (such as calling sleep, wait, join, etc.), and the interrupt state will also be cleared, so that the thread can exit the blocked state. The following is the specific implementation:

class Example3 extends Thread {
    public static void main(String args[]) throws Exception {
        Example3 thread = new Example3();
        System.out.println("Starting thread...");
        thread.start();
        Thread.sleep(3000);
        System.out.println("Asking thread to stop...");
        thread.interrupt();// Call after the interrupt semaphore is set
        Thread.sleep(3000);
        System.out.println("Stopping application...");
    }

    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("Thread running...");
            try {
                /*
                 * If the thread is blocked, the interrupt semaphore stop variable will not be checked, so thread interrupt()
                 * It will cause the blocking thread to throw an exception from the blocking place, make the blocking thread escape from the blocking state, and
                 * The abnormal block is processed accordingly
                 */
                Thread.sleep(1000);// Thread blocking. If the thread receives an interrupt operation signal, it will throw an exception
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted...");
                /*
                 * If the thread is calling object Wait () method, or join() and sleep() methods of this class
                 * If the process is blocked, its interrupt status will be cleared
                 */
                System.out.println(this.isInterrupted());// false

                //It is up to you to decide whether to interrupt the thread. If you need to interrupt the thread, you need to reset the interrupt bit. If
                //If not, no call is required
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("Thread exiting under request...");
    }
}

Once the thread in Example3 When interrupt () is called, the thread receives an exception, escapes the blocking state and determines that it should stop. Above, we can also use shared semaphores to replace! Thread.currentThread().isInterrupted() condition, but not as concise as it is.

Deadlock thread cannot be interrupted

Example4 tries to interrupt two threads in deadlock state, but neither of them receives any interrupt signal (throw an exception), so the interrupt() method cannot interrupt the deadlock thread, because the locked position cannot throw an exception at all:

class Example4 extends Thread {
    public static void main(String args[]) throws Exception {
        final Object lock1 = new Object();
        final Object lock2 = new Object();
        Thread thread1 = new Thread() {
            public void run() {
                deathLock(lock1, lock2);
            }
        };
        Thread thread2 = new Thread() {
            public void run() {
                // Notice that there is a change of position here
                deathLock(lock2, lock1);
            }
        };
        System.out.println("Starting thread...");
        thread1.start();
        thread2.start();
        Thread.sleep(3000);
        System.out.println("Interrupting thread...");
        thread1.interrupt();
        thread2.interrupt();
        Thread.sleep(3000);
        System.out.println("Stopping application...");
    }

    static void deathLock(Object lock1, Object lock2) {
        try {
            synchronized (lock1) {
                Thread.sleep(10);// I won't die here
                synchronized (lock2) {// It will be locked here. Although it is blocked, it will not throw exceptions
                    System.out.println(Thread.currentThread());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

Interrupt I/O operation

However, what happens if a thread is blocked while an I/O operation is in progress? I/O operations can block threads for a long time, especially when involving network applications. For example, the server may have to wait for a request, or a network application may have to wait for a response from a remote host.

The channel implementing this interruptible channel interface is interruptible: if a thread invokes a blocked I/O operation on the interruptible channel (common operations are: serverSocketChannel.accept(), socketchannel connect,socketChannel.open,socketChannel.read,socketChannel.write,fileChannel.read,fileChannel.write), and another thread calls the interrupt method of the blocked thread, which will cause the channel to be closed, and the blocked thread will receive a closedbyinteruptexception, and set the interrupt state of the blocked thread.

In addition, if the interrupt state of a thread has been set and it calls a blocked I/O operation on the channel, the channel will be closed and the thread will immediately receive closedbyinteruptexception; And still set its interrupt state. If this is the case, the logic of the code is the same as that in the third example, but it is abnormally different.

If you are using channels (a new I/O API introduced in Java 1.4), the blocked thread will receive a closedbyinteruptexception. However, you may be using java 1 Traditional I/O existed before 0 and requires more work. In that case, thread interrupt() will not work because the thread will not exit the blocked state. Example 5 describes this behavior. Although interrupt() is called, the thread will not exit the blocked state. For example, the accept method of ServerSocket does not throw an exception at all.

Fortunately, the Java platform provides a solution to this situation by calling the close() method that blocks the socket of the thread. In this case, if the thread is blocked by I/O operation, when calling the close method of the socket, the thread will receive a SocketException (SocketException is the sub exception of IOException) exception when calling the accept method, which is very similar to using the interrupt() method to cause an InterruptedException exception to be thrown, (note that if the flow is blocked by read and write, the close method of calling the stream will also be blocked. It can not be invoked at all, and will not be thrown IOExcepiton. How can I interrupt it? I think it can be converted to a channel to operate the stream, such as file passages). The following is the specific implementation:

class Example6 extends Thread {
    volatile ServerSocket socket;

    public static void main(String args[]) throws Exception {
        Example6 thread = new Example6();
        System.out.println("Starting thread...");
        thread.start();
        Thread.sleep(3000);
        System.out.println("Asking thread to stop...");
        Thread.currentThread().interrupt();// Then call the interrupt method
        thread.socket.close();// Then call the close method
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }
        System.out.println("Stopping application...");
    }

    public void run() {
        try {
            socket = new ServerSocket(8888);
        } catch (IOException e) {
            System.out.println("Could not create the socket...");
            return;
        }
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("Waiting for connection...");
            try {
                socket.accept();
            } catch (IOException e) {
                System.out.println("accept() failed or interrupted...");
                Thread.currentThread().interrupt();//Reset interrupt flag bit
            }
        }
        System.out.println("Thread exiting under request...");
    }
}

1, Without any language requirements, an interrupted thread should be terminated. Interrupting a thread is just to attract the attention of the thread. The interrupted thread can decide how to deal with the interrupt.

2, For threads in sleep, join and other operations, if interrupt() is called, InterruptedException will be thrown, and then the interrupt flag bit of the thread will be reset from true to false, because the thread has been ready again to handle exceptions.

3, Non interruptible operations, including entering synchronized section and lock lock(),inputSteam.read(), calling interrupt() is invalid for these problems, because they do not throw interrupt exceptions. If resources are not available, they will block indefinitely.

For lock Lock(), you can use lock instead Lockinterruptible(), which can be used to interrupt the locking operation, can throw interrupt exceptions. Equivalent to lock with infinite waiting time tryLock(long time, TimeUnit unit).

For inputStream and other resources, some (implementing the interruptibleChannel interface) can close the resources through the close() method, and the corresponding blocking will be released.

First, let's look at several methods in the Thread class:

Several interrupt related methods and their behaviors are listed above. You can see that interrupt is an interrupt thread. If you don't understand the interrupt mechanism of Java, such an explanation is very easy to cause misunderstanding. You think that calling the interrupt method of the thread will interrupt the thread.

In fact, Java interrupt is a cooperative mechanism. In other words, calling the interrupt method of the Thread object does not necessarily interrupt the running Thread. It just requires the Thread to interrupt itself at the right time. Each Thread has a boolean interrupt state (this state is not on the Thread attribute), and the interrupt method only sets this state to true.

For example, calling interrupt() on a running thread does not terminate it, but changes the interrupt flag.

Generally speaking, if a method declares that it throws InterruptedException, it means that the method is interruptible, such as wait, sleep and join. That is to say, the interruptible method will respond to the interrupt call (for example, sleep's response to interrupt includes clearing the interrupt state and throwing InterruptedException). All exceptions are thrown by the interruptible method itself, It is not directly caused by the interrupt method.

Object. wait, Thread. The sleep method will continuously poll and listen for the interrupted flag bit. When it is found that it is set to true, it will stop blocking and throw an InterruptedException exception.

After reading the above instructions, I'm sure I can use java interrupts, but what I want to know is how blocked threads stop blocking and throw interruptedException through the interrupt method, which depends on the native interrupt 0 method in Thread.

The first step is to learn that JNI of Java calls the Native method.

Step 2 download the source code of openjdk and find openjdk SRC \ JDK \ SRC \ share \ native \ Java \ Lang \ thread in the directory structure C documents.

#include "jni.h"
#include "jvm.h"

#include "java_lang_Thread.h"

#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"

#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
};

#undef THD
#undef OBJ
#undef STE
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

Author: Zero_ One\
Source: https://blog.csdn.net/xinxiao...

Recent hot article recommendations:

1.1000 + Java interview questions and answers (2022 latest version)

2.Hot! The Java collaboration is coming...

3.Spring Boot 2.x tutorial, too complete!

4.20w programmer red envelope cover, get it quickly...

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don't forget to like + forward!

Topics: Java