The difference between interrupt, interrupted and isInterrupted

Keywords: Java

Today I saw that the isInterrupted method of the Thread class can get the interrupt state of the thread:

So I wrote an example to prove that:

public class Interrupt {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Worker());
        t.start();

        Thread.sleep(200);
        t.interrupt();

        System.out.println("Main thread stopped.");
    }

    public static class Worker implements Runnable {
        public void run() {
            System.out.println("Worker started.");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println("Worker IsInterrupted: " + 
                        Thread.currentThread().isInterrupted());
            }

            System.out.println("Worker stopped.");
        }
    }
}

The main thread main starts a sub-thread Worker, then lets the worker sleep 500 ms, while the main sleeps 200 ms. Then the main calls the interrupt method of the worker thread to interrupt the worker, and the worker prints the interrupt status after interruption. The following are the results of implementation:

Worker started.
Main thread stopped.
Worker IsInterrupted: false
Worker stopped.

Worker clearly has been interrupted, and the isInterrupted() method actually returns false. Why?

After searching stack overflow, I found that some netizens mentioned that they could view JavaDoc (or source code) which throws the InterruptedException method. So I looked at the document of Thread.sleep method, which is described in the doc as follows:

InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

Notice the following sentence: "When this exception is thrown, the interrupt state is cleared". So the isInterrupted() method should return false. But sometimes, we need the isInterrupted method to return true. What can we do? Let's start with the difference between interrupt, interrupted and isInterrupted.

The interrupt method is used to interrupt threads, and the state of the thread calling the method will be set to "interrupt" state. Note: Thread interruption only sets the interrupt status bit of the thread and does not stop the thread. Users are required to monitor the status of threads themselves and process them. The way to support thread interruption (i.e. the method that threads throw InterruptedException after interruption, such as sleep here, Object.wait, etc.) is to monitor the interruption state of threads. Once the interruption state of threads is set to "interrupt state", an interruption exception will be thrown. This view can be confirmed by this article:

interrupt() merely sets the thread's interruption status. Code running in the interrupted thread can later poll the interrupted status to see if it has been requested to stop what it is doing

Let's look at the implementation of interrupted:

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

And the implementation of isInterrupted:

public boolean isInterrupted() {
    return isInterrupted(false);
}

The two methods are static and not, but they are actually calling the same method, except that the interrupted method passes in a parameter of true and the inInterrupted method passes in a parameter of false. So what exactly does this parameter mean? Let's look at the implementation of this isInterrupted(boolean) method:

/**
 * Tests if some Thread has been interrupted.  The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
private native boolean isInterrupted(boolean ClearInterrupted);

This is a native method. It doesn't matter if you can't see the source code. The parameter name ClearInterrupted clearly expresses the function of the parameter - whether to clear the interrupt state. The annotations to the method also clearly state that "the interrupt state will be reset based on the value of the incoming ClearInterrupted parameter". Therefore, the static method interrupted will clear the interrupt state (the parameter ClearInterrupted is true), while the instance method isInterrupted will not (the parameter ClearInterrupted is false).

Back to the question just now: Obviously, if you want the isInterrupted method to return true, you can restore the status of the interrupt by calling the interrupt() method again before calling the isInterrupted method:

public class Interrupt  {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Worker());
        t.start();

        Thread.sleep(200);
        t.interrupt();

        System.out.println("Main thread stopped.");
    }

    public static class Worker implements Runnable {
        public void run() {
            System.out.println("Worker started.");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread curr = Thread.currentThread();
                //Call the interrupt method again to interrupt itself and set the interrupt state to "interrupt"
                curr.interrupt();
                System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
                System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
                System.out.println("Static Call: " + Thread.interrupted());//clear status
                System.out.println("---------After Interrupt Status Cleared----------");
                System.out.println("Static Call: " + Thread.interrupted());
                System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
                System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
            }

            System.out.println("Worker stopped.");
        }
    }
}

Implementation results:

Worker started.
Main thread stopped.
Worker IsInterrupted: true
Worker IsInterrupted: true
Static Call: true
-—After Interrupt Status Cleared-—-
Static Call: false
Worker IsInterrupted: false
Worker IsInterrupted: false
Worker stopped.
As can be seen from the results of execution, the first two calls to the isInterrupted method all return true, indicating that the isInterrupted method will not change the interrupt state of the thread, while the next call to the static interrupted() method returns true for the first time, indicating that the thread has been interrupted, and false for the second time, because the interrupt state has been cleared at the first call. The last two calls to the isInterrupted() method are bound to return false.

In what scenario, then, do we need to interrupt threads (reset interrupt status) in the catch block?

The answer is: If you can't throw an InterruptedException (like the Thread.sleep statement in Runnable's run method, which does not allow throwing any checked exceptions), but want to tell the upper caller that an interrupt has occurred here, you can only reset the interrupt state in catch.

The following is from Dealing with Interrupted Exception

If you catch InterruptedException but cannot rethrow it, you should preserve evidence that the interruption occurred so that code higher up on the call stack can learn of the interruption and respond to it if it wants to. This task is accomplished by calling interrupt() to "reinterrupt" the current thread, as shown in Listing 3.

 Listing 3:  Restoring the interrupted status after catching InterruptedException

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         } catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

So the question arises: Why do you want to clear the interrupt state when you throw InterruptedException?

There is no official explanation for this question, and it is estimated that only Java designers will be able to answer it. But the explanation here seems reasonable: an interrupt should only be handled once (you catch this Interrupted Exception, which means you can handle the exception, and you don't want the upper caller to see the interrupt).

If there is a better explanation for this problem, please leave a message for discussion.

                                                       Reprinted from ferry

Posted by anhedonia on Fri, 07 Jun 2019 15:58:23 -0700