Safely and gracefully stop Java threads

Wen / Zhu Jiqian

First, throw a question, how to stop a thread safely and gracefully?

This problem can be small, ranging from simply terminating a Thread thread to large, such as the elegant offline of Dubbo application... They actually have one thing in common, that is, they do not stop a process or Thread violently, but can have the opportunity to clean up resources and finish the remaining tasks during the termination process, Finally, no resources are running before the final end, which is a safe and elegant stop.

In Java multithreading, there is a method to stop threads that has expired and is not recommended. The method to stop threads is relatively simple and rough. It stops directly without ensuring the normal release of thread resources, which means that the thread may be running and terminate directly before it is completed, This may lead to an uncertain state of the program, that is, a deadlock state.

The method of terminating the thread by stop has expired, that is, it is no longer recommended.

So, is there any other way to end a thread gracefully?

Here, it can be implemented indirectly through the interrupt() method.

Why is it a simple implementation?

Because the thread executing the interrupt() method does not directly terminate the thread.

Next, let's briefly analyze how interrupt() can safely and gracefully terminate a thread.

First of all, after executing the interrupt() method of a thread, an interrupt identification attribute will be marked on the thread. The identification attribute was originally false, but after being marked with the interrupt identification, it will become true. This is somewhat similar to the visibility play of volatile variables. Through such visibility variables, we can set a state. When the state is satisfied, You can jump out of the program and end early.

You can obtain the status value of the interrupt identification attribute through the isInterrupted() method. If true, it means that the thread has been marked with an interrupt identification. Then, you can clean up the resources before ending the thread.

However, it should be noted that there is a similar static method, Thread.interrupted(), which can also obtain the thread interrupt state. Unfortunately, this interrupted method will immediately reset the interrupt state of the thread after judging whether the thread is interrupted, that is, restore the thread to the non interrupted state. In addition, declaring the method that throws InterruptedException will also clear the interrupt identification status of the thread through the virtual machine before throwing an exception, and then throw an exception. At this time, calling isInterrupted() method returns false.

Here's the code to verify it——

public static void main(String[] args) throws InterruptedException {
    Runner one = new Runner();
    Thread countThread = new Thread(one,"CountThread");
    //Start thread
    countThread.start();
    //Silence for one second and let the thread count thread execute for one second
    TimeUnit.SECONDS.sleep(1);
    //Set the interrupt ID on the thread countThread through the interrupt() method
    countThread.interrupt();
}
​
private static class Runner implements Runnable {
    private long i;
    private volatile boolean on = true;
    @Override
    public void run() {
        //When the countThread thread identifies an interrupt, Thread.currentThread().isInterrupted() returns true to end the thread and stop the continuous operation of resource i + +
        while (!Thread.currentThread().isInterrupted()){
            i++;
        }
        System.out.println("Count i = " + i);
    }
}

As mentioned earlier, interrupt() identifies the interrupt bit, which is very similar to the visibility of volatile variables. Conversely, volatile can also replace interrupt() to determine whether a thread needs to be interrupted to some extent. Similar codes are as follows——

public static void main(String[] args) throws InterruptedException {
    Runner two = new Runner();
    Thread countThread = new Thread(two,"CountThread");
    countThread.start();
    //Sleep for 1 second
    TimeUnit.SECONDS.sleep(1);
    two.cancel();
​
}
​
private static class Runner implements Runnable {
    private long i;
    private volatile boolean on = true;
    @Override
    public void run() {
        while (on){
            i++;
        }
        System.out.println("Count i = " + i);
    }
​
    public void cancel(){
        on = false;
    }
}

Tags: OOP

Posted on Sun, 05 Dec 2021 10:43:20 -0500 by adi