I. Java interruption
First, let's look at several methods in the Thread class:
| Public static booleanInterrupted |
Test whether the current thread has been interrupted. This method clears the thread interruption status. In other words, if this method is called twice in a row, false will be returned for the second call (after the first call has been cleared, and the second call has completed the interruption state, except when the current thread is interrupted again ). |
| Public booleanIsInterrupted() |
Test whether the thread has been interrupted. The thread interruption status is not affected by this method. |
| Public voidInterrupt() |
Interrupt thread. |
The above lists several interrupt-related methods and their behavior. We can see that interrupt is the interrupt thread. If you do not understand the interrupt mechanism of Java, such an explanation is very easy to misunderstand. It is believed that the interrupt method that calls the thread will definitely interrupt the thread.
In fact, Java interrupt is a collaboration mechanism. That is to say, the interrupt method of the calling thread object does not necessarily interrupt the running thread. It only requires the thread to interrupt itself at the right time. Each Thread has a boolean interrupt state (not necessarily an object attribute. In fact, this state is indeed not a Thread field). The interrupt method only sets this state to true.Copy codeThe Code is as follows: public class TestInterrupt {
Public static void main (String [] args ){
Thread t = new MyThread ();
T. start ();
T. interrupt ();
System. out. println ("the interrupt method of the called thread ");
}
Static class MyThread extends Thread {
Public void run (){
Int num = longTimeRunningNonInterruptMethod (2, 0 );
System. out. println ("long-time task running ended, num =" + num );
System. out. println ("Thread interruption status:" + Thread. interrupted ());
}
Private static int longTimeRunningNonInterruptMethod (int count, int initNum ){
For (int I = 0; I <count; I ++ ){
For (int j = 0; j <Integer. MAX_VALUE; j ++ ){
InitNum ++;
}
}
Return initNum;
}
}
}
In general, the following content is printed:
The interrupt method of the called thread
The task has been running for a long time. num =-2
Thread interruption status: true
It can be seen that the interrupt method does not necessarily interrupt the thread. However, what will happen if I change it to the following program?Copy codeThe Code is as follows: import java. util. concurrent. TimeUnit;
Public class TestInterrupt {
Public static void main (String [] args ){
Thread t = new MyThread ();
T. start ();
T. interrupt ();
System. out. println ("the interrupt method of the called thread ");
}
Static class MyThread extends Thread {
Public void run (){
Int num =-1;
Try {
Num = longTimeRunningInterruptMethod (2, 0 );
} Catch (InterruptedException e ){
System. out. println ("thread interrupted ");
Throw new RuntimeException (e );
}
System. out. println ("long-time task running ended, num =" + num );
System. out. println ("Thread interruption status:" + Thread. interrupted ());
}
Private static int longTimeRunningInterruptMethod (int count, int initNum) throws InterruptedException {
For (int I = 0; I <count; I ++ ){
TimeUnit. SECONDS. sleep (5 );
}
Return initNum;
}
}
}
After running, we can find that the program throws an exception and stops. The last two print statements in the run method are not executed. So what is the difference?
Generally, if a method declaration throws InterruptedException, it indicates that the method is interrupted (except when InterruptedException is thrown but not handled in the method ), that is to say, the method that can be interrupted will respond to interrupt calls (for example, sleep responds to interrupt operations, including clearing the interrupt status and throwing InterruptedException). If interrupt is called before the method that can be interrupted, the interrupt method is bound to handle the interrupt. As in the above example, the interrupt method is probably called when run does not enter sleep, but when sleep detects the interrupt, it will handle the interrupt. What if I call interrupt when the method that can be interrupted is being executed? This depends on the time when the interrupt method can process the interrupt. As long as the interrupt method can detect that the interrupt status is true, the interrupt should be handled. Let's add Interrupt Processing for the code at the beginning.
In this case, how can we handle the interruption in a custom method that can be interrupted? That is, the thread interruption status is detected and processed in a suitable place to handle the interruption.Copy codeThe Code is as follows: public class TestInterrupt {
Public static void main (String [] args) throws Exception {
Thread t = new MyThread ();
T. start ();
// TimeUnit. SECONDS. sleep (1); // if you cannot see the interrupted state during the processing, you can enable this sentence to check the effect.
T. interrupt ();
System. out. println ("the interrupt method of the called thread ");
}
Static class MyThread extends Thread {
Public void run (){
Int num;
Try {
Num = longTimeRunningNonInterruptMethod (2, 0 );
} Catch (InterruptedException e ){
Throw new RuntimeException (e );
}
System. out. println ("long-time task running ended, num =" + num );
System. out. println ("Thread interruption status:" + Thread. interrupted ());
}
Private static int longTimeRunningNonInterruptMethod (int count, int initNum) throws InterruptedException {
If (interrupted ()){
Throw new InterruptedException ("the thread has been interrupted by the request before the formal processing ");
}
For (int I = 0; I <count; I ++ ){
For (int j = 0; j <Integer. MAX_VALUE; j ++ ){
InitNum ++;
}
// If this is a suitable place
If (interrupted ()){
// Rollback data and cleanup operations
Throw new InterruptedException ("the thread is being interrupted during processing ");
}
}
Return initNum;
}
}
}
The code above indicates that the longTimeRunningMethod method is an interrupt method. When you enter the method, determine whether the request is interrupted. If yes, no corresponding processing will be performed. During the process, there may also be a suitable place to handle the interruption, for example, after the inmost loop ends.
Interrupted, a static Thread method, is used to detect interruptions in this Code. It sets the interrupt status to false and returns the previous status. isInterrupted only detects interruptions and does not change the interrupt status. In general, if an interrupted request has been processed, set its status to false. But it depends on the actual situation.
Ii. essence of Java interrupt
In history, Java has tried to provide preemptible restrictions on interruptions, but there are many problems, such as obsolete Thread. stop, Thread. suspend, and Thread. resume. On the other hand, for the sake of the robustness of Java application code, the programming threshold is reduced, and the probability that programmers who do not know the underlying mechanism have no intention to destroy the system is reduced.
Currently, Java Thread Scheduling does not provide preemptive interruptions, but uses collaborative interruptions. In fact, the principle of collaborative interruption is very simple, that is, polling a mark indicating the interruption, which can be implemented in any common code. For example, the following code:Copy codeThe Code is as follows: volatile bool isInterrupted;
//...
While (! IsInterrupted ){
Compute ();
}
However, the above Code problems are also obvious. When the execution time of compute is relatively long, the interruption cannot be responded in time. On the other hand, using the polling check flag variable method, it is useless to interrupt thread blocking operations such as wait and sleep.
If you still use the above ideas to make the interrupt respond in a timely manner, you must perform thread scheduling at the underlying layer of the virtual machine to check the tag variables. Yes, it does in JVM. The following source code is taken from java. lang. Thread:Copy codeThe Code is as follows: public static boolean interrupted (){
Return currentThread (). isInterrupted (true );
}
//...
Private native boolean isInterrupted (boolean ClearInterrupted );
It can be found that isInterrupted is declared as the native method, depending on the implementation of the JVM underlying layer.
In fact, the JVM maintains an interrupt mark for each thread. However, the application cannot directly access this interrupt variable. You must perform the following operations:Copy codeThe Code is as follows: public class Thread {
// Set the interrupt mark
Public void interrupt (){...}
// Obtain the value of the interrupt mark
Public boolean isInterrupted (){...}
// Clear the interrupt mark and return the value of the last interrupt mark
Public static boolean interrupted (){...}
...
}
Generally, calling the thread's interrupt method does not immediately cause an interruption, but only sets the interrupt mark inside the JVM. Therefore, by checking the interrupt tag, the application can perform some special operations or ignore the interrupt completely.
You may think that if the JVM only provides this simple interrupt mechanism, there is basically no advantage over the method in which the application defines the interrupt variable and polls itself.
The main advantage of JVM interrupt variables is that in some cases, it provides a mechanism to simulate automatic "interrupt.
When executing a blocking call involving Thread Scheduling (such as wait, sleep, and join), if the thread is interrupted, InterruptedException will be thrown as fast as possible. Therefore, we can use the following code framework to handle thread blocking interruptions:Copy codeThe Code is as follows: try {
// Wait, sleep, or join
}
Catch (InterruptedException e ){
// Some interrupt handling work
}
The so-called "as fast as possible", I guess JVM is to check the interrupt variable in the gap between thread scheduling and scheduling. The speed depends on JVM implementation and hardware performance.
Iii. Some thread blocking operations that do not throw InterruptedException
However, for some thread blocking operations, JVM does not automatically throw an InterruptedException exception. For example, some I/O operations and internal lock operations. For such operations, you can use other methods to simulate interruptions:
1) asynchronous socket I/O in java. io
During socket reading and writing, the read and write methods of InputStream and OutputStream will block the wait, but will not respond to java interruption. However, after the Socket close method is called, The blocked thread will throw a SocketException.
2) asynchronous I/O implemented by Selector
If the thread is blocked in Selector. select (in java. nio. channels), calling the wakeup method causes ClosedSelectorException.
3) Lock acquisition
If the thread is waiting to obtain an internal lock, we cannot interrupt it. However, with the lockInterruptibly method of the Lock class, we can provide the Interrupt Capability while waiting for the Lock.
Iv. Two programming principles
In addition, in the framework of separation of tasks and threads, tasks generally do not know which thread they will be called, and they do not know the policy of calling threads to handle interruptions. Therefore, after the thread interrupt mark is set for the task, the task cannot be canceled. Therefore, there are two programming principles:
1) unless you know the thread interruption policy, you should not interrupt it.
This principle tells us that the interrupt method of threads in the framework such as Executer should not be called directly, and tasks such as Future. cancel should be canceled.
2) The task code should not guess the meaning of the interruption on the execution thread.
This principle tells us that when the Code encounters an InterruptedException exception, it should not be captured and "swallowed up", but should continue to throw the code above the layer.
In short, the non-preemptible interrupt mechanism in Java requires us to change the traditional idea of preemptible interrupt and adopt corresponding principles and patterns for programming on the basis of understanding its nature.