Java method to terminate the thread correctly

There is a deprecated stop() method in the Thread class, which can terminate the thread, but because it directly terminates the thread regardless of the three or seven twenty one, it is deprecated. For example, after the thread is stopped, some aftermath operations (such as shutting down external resources) are required, and there is nothing you can do with this method. Thread termination can be achieved by thread interruption.

First look at some of the contents of Java thread interruption:

  • The Java platform maintains a boolean interrupt flag for each thread. The value of the flag can be obtained by the following methods:
    interrupt() interrupts a thread
    isInterrupted() returns the interrupt flag of the thread
    interrupted() returns and resets the thread Interrupt flag (set to false)

  • Interruption is only a request from the initiating thread to the target thread, that is, the target thread can respond to this request or ignore it.

  • The methods in the Java standard library related to thread blocking respond to interruption by throwing InterruptedException, and by convention, the interrupt flag is reset to false before throwing an exception, so these methods will clear the interrupt flag of the thread.

  • The methods related to thread blocking in the Java standard library will judge whether the interrupt flag is true before blocking, and throw an exception if it is true; if the interrupt method is called after blocking, then the JVM will set the interrupt flag of the thread, and then The thread wakes up, so the interrupt has the effect of waking up the thread.

From the above points and the second sentence in bold, it can be seen that thread interruption can be used to achieve thread termination, as long as the target thread judges the interrupt mark, even if the interrupted thread is in a blocked state, it can be awakened and terminated; From the first sentence in bold, it can be seen that directly using thread interrupts to achieve thread termination is risky, because some blocking methods of the Java standard library may be called, which causes the interrupt flag to be cleared, and the interrupt flag cannot be obtained (total Is false), so you need to create an interrupt flag to use it.

For example, the following is an interruptible task executor. Before each task is executed, it will judge the termination mark of the self-defined i and the number of remaining tasks (aftermath); the shutdown method provided is in addition to interrupting the worker thread (mainly The function is to wake up tasks that may be in a blocked state), and set the termination intersection terminated to true.

Executing the main method, you can find that it will print out "The client has called the shutdown method" first, and then the main thread will terminate after four seconds. It can be seen that the shutdown method correctly terminates the target thread. Regarding "According to the convention, the thread-related blocking methods that throw InterruptedException in the Java standard library will clear the interrupt mark", you can replace !interminated in the condition with !Thread.currentThread().isInterrupted(), and then execute main Method test, it can be found that the main thread cannot be terminated, because the sleep() method clears the interrupt flag, so !Thread.currentThread().isInterrupted() is always true, resulting in the worker thread being unable to terminate.

public class TerminableTaskRunner {
    // 存储要执行的任务
    private final BlockingQueue<Runnable> tasks;
    // 线程终止标志
    private volatile boolean terminated;
    // 剩余的任务数
    private final AtomicInteger count;
    // 实际执行任务的线程
    private volatile Thread workThread;
 
    public TerminableTaskRunner(int capacity) {
        this.tasks = new LinkedBlockingDeque<>(capacity);
        this.count = new AtomicInteger(0);
        this.workThread = new WorkThread();
        workThread.start();
    }
 
    public void submit(Runnable task) {
        this.tasks.add(task);
        this.count.incrementAndGet();
    }
 
    public void shutdown() {
        terminated = true; // 线程终止标志,由于中断标志可能会被覆盖,所以需要自己创建一个标志
        if (workThread != null)
            workThread.interrupt(); // 唤醒线程
    }
 
    private class WorkThread extends Thread {
        @Override
        public void run() {
            Runnable task;
            try {
                while (!terminated || tasks.size() >= 1) {
                    task = tasks.take();
                    try {
                        task.run(); // 可能会清空当前线程的中断标记,如task.run()在内部调用的阻塞方法抛出了InterruptedException
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                    count.decrementAndGet();
                }
            } catch (InterruptedException e) {
                // 一旦调用shutdown且tasks.take()阻塞住,就抛出该异常,没有任务要执行,直接终止
                workThread = null;
            }
        }
    }
 
    public static void main(String[] args) {
        TerminableTaskRunner taskRunner = new TerminableTaskRunner(4);
        for (int i = 0; i < 4; i++) {
            taskRunner.submit(()->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("客户端调用了 shutdown 方法");
                }
            });
        }
        taskRunner.shutdown();
 
    }
}

Some high-frequency interview questions collected in the latest 2020 (all organized into documents), there are many dry goods, including mysql, netty, spring, thread, spring cloud, jvm, source code, algorithm and other detailed explanations, as well as detailed learning plans, interviews Question sorting, etc. For those who need to obtain these contents, please add Q like: 11604713672

Guess you like

Origin blog.csdn.net/weixin_51495453/article/details/114754567