Thread interruption and violent stop

Thread interruption

Thread insecurity of stop() method

The Thread.stop() method can stop a running thread, but this method has been deprecated. Why?

First look at the following piece of code:

package com.morris.concurrent.thread.interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 演示stop()方法带来的线程安全问题
 */
public class StopDemo {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        Object lock = new Object();

        Thread t1 = new Thread(() -> {
    
    
            try {
    
    
                synchronized (lock) {
    
    
                    System.out.println("thread->" + Thread.currentThread().getName() + " acquire lock.");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println("thread->" + Thread.currentThread().getName() + " 等待3s");
                    System.out.println("thread->" + Thread.currentThread().getName() + " release lock.");

                }
            } catch (Throwable e) {
    
     // 抛出ThreadDeath Error
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
    
    
            synchronized (lock) {
    
    
                System.out.println("thread->" + Thread.currentThread().getName() + " acquire lock.");
            }
        });

        t1.start();

        // 休眠100ms,等待t1线程运行
        TimeUnit.MILLISECONDS.sleep(100);

        t2.start();

        t1.stop();
        
    }
}

The results are as follows:

thread->Thread-0 acquire lock.
thread->Thread-1 acquire lock.
java.lang.ThreadDeath
	at java.lang.Thread.stop(Thread.java:853)
	at com.morris.concurrent.thread.interrupt.StopDemo.main(StopDemo.java:41)

From the running results, we can see that after calling the t1.stop() method, we can find that the t1 thread threw a ThreadDeath Error and the t1 thread released the lock it held.

Generally, any code block for locking is to protect the consistency of the data. If the call to thread.stop() causes the sudden release (uncontrollable) of all the locks held by the thread, then the protected data is There may be inconsistencies, and other threads may cause some strange application errors when using the corrupted data.

This is also kill -9the reason why the use of commands to force a thread to be killed is not recommended , which may lead to some difficult thread safety issues.

interrupt() interrupt thread

In addition to thread safety issues, the stop() method is a mandatory termination for the terminated thread. In Java, a kind of collaboration is advocated between threads.

Each thread has a boolean type flag to indicate whether the current thread is interrupted. The interrupt() method is used to set the interrupt flag bit of the thread to true, and the interrupted thread can decide by itself whether to process the interrupt (directly terminate or terminate or not terminate after processing such as data protection).

We can call Thread.currentThread().isInterrupted() or Thread.interrupted() to detect whether the thread's interrupt flag is set.

The difference between these two methods is:

  • Thread.currentThread().isInterrupted(): Object method, the interrupt flag bit is not cleared.
  • Thread.interrupted(): Static method, clear the interrupt flag bit.

So calling the interrupt() method of a thread will not interrupt a running thread. This mechanism just sets a thread interrupt flag bit. If you don’t check the thread interrupt flag bit in the program, even if the interrupt flag bit is set to true , The thread runs as usual.

Generally speaking, interrupt threads are divided into three situations:

  1. Interrupt non-blocking threads
  2. Interrupt blocking thread
  3. Interrupt uninterruptible thread

Interrupt non-blocking threads

Thread shared variables

This method is relatively simple and feasible. One thing to note is that shared variables must be set to volatile, so as to ensure that other threads are immediately visible after modification.

package com.morris.concurrent.thread.interrupt;

/**
 * 中断非阻塞的线程:使用共享变量
 */
public class InterruptNonBlockWithShareVariable extends Thread {
    
    

    // 设置线程共享变量
    private volatile boolean isStop = false;

    @Override
    public void run() {
    
    
        while(!isStop) {
    
    
            long beginTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + " is running");
            // 当前线程每隔一秒钟检测一次线程共享变量是否得到通知
            while (System.currentTimeMillis() - beginTime < 1000) {
    
    }
        }
        if (isStop) {
    
    
            System.out.println(Thread.currentThread().getName() + " is interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        InterruptNonBlockWithShareVariable thread = new InterruptNonBlockWithShareVariable();
        thread.start();

        Thread.sleep(5000);
        thread.isStop = true;

    }
}

Use interrupt mechanism

package com.morris.concurrent.thread.interrupt;

/**
 * 中断非阻塞的线程:使用中断机制
 */
public class InterruptNonBlockWithInterrupt extends Thread {
    
    

    @Override
    public void run() {
    
    
        while(!Thread.currentThread().isInterrupted()) {
    
    
            long beginTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + " is running");
            // 当前线程每隔一秒钟检测一次线程共享变量是否得到通知
            while (System.currentTimeMillis() - beginTime < 1000) {
    
    }
        }

         System.out.println(Thread.currentThread().getName() + " interrupted flag:" + Thread.currentThread().isInterrupted());
         System.out.println(Thread.currentThread().getName() + " is interrupted");
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        InterruptNonBlockWithInterrupt thread = new InterruptNonBlockWithInterrupt();
        thread.start();

        Thread.sleep(5000);
        thread.interrupt();

    }
}

Interrupt blocking thread

When a thread calls Thread.sleep(), Thread.join(), Object.wait(), LockSupport.park() or calls a blocking i/o operation method, it will cause the current thread to enter the blocking state.

If the thread is in a blocked state, an InterruptedException will be thrown when the interrupt() method is called to set the thread interrupt flag bit, and the thread interrupt flag bit will be cleared (set to false), so that the thread can exit the blocked state.

If the thread has been interrupted and then the blocking method is called, an exception will be thrown directly.

Use shared variables

package com.morris.concurrent.thread.interrupt;

/**
 * 中断阻塞的线程:使用共享变量
 * 缺点:使用共享变量无法及时中断阻塞线程,因为线程不能第一时间接到中断通知,需等线程被唤醒时才知道。
 */
public class InterruptBlockThreadWithVariable extends Thread {
    
    

    // 设置线程共享变量
    private volatile boolean isStop = false;

    @Override
    public void run() {
    
    
        while(!isStop) {
    
    
            System.out.println(Thread.currentThread().getName() + " is running");
            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
            }
        }
        System.out.println(Thread.currentThread().getName() + " is interrupted");
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        InterruptBlockThreadWithVariable thread = new InterruptBlockThreadWithVariable();
        thread.start();

        Thread.sleep(1000);
        thread.isStop = true;

    }
}

Disadvantages: The use of shared variables cannot interrupt the blocked thread in time, because the thread cannot be notified of the interruption in the first time, and it needs to be known when the thread is awakened.

Use interrupt mechanism

package com.morris.concurrent.thread.interrupt;

/**
 * 中断阻塞的线程:使用中断机制
 * 注意抛出异常会清除中断标志位
 */
public class InterruptBlockThreadWithInterrupt extends Thread {
    
    

    @Override
    public void run() {
    
    
        // 这里调用的是非清除中断标志位的isInterrupted方法
        while(!Thread.currentThread().interrupted()) {
    
    
            System.out.println(Thread.currentThread().getName() + " is running");
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                // 由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去
                Thread.currentThread().interrupt();
            }
        }
        System.out.println(Thread.currentThread().getName() + " is interrupted");
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        InterruptBlockThreadWithInterrupt thread = new InterruptBlockThreadWithInterrupt();
        thread.start();

        Thread.sleep(5000);
        thread.interrupt();

    }
}

Brute force to stop uninterruptible threads

package com.morris.concurrent.thread.interrupt;

import java.util.concurrent.TimeUnit;

/**
 * 暴力停止不可中断的线程,如正在获取synchronized锁的线程
 */
public class ViolenceStopNotInterruptThread {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        ThreadService threadService = new ThreadService();
        threadService.execute(() -> {
    
    
            while (true) {
    
    
                System.out.println("I am working...");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }

        });

        // 等待threadService里面的线程充分运行
        TimeUnit.MILLISECONDS.sleep(1000);

        threadService.shutdown(1_000);

    }

    private static class ThreadService {
    
    

        // 这个线程负责启动一个守护线程,当这个线程结束时,它创建的线程也会随之结束
        private Thread executeThread;

        // 用于标记任务是否结束
        private boolean isStop = false;

        /**
         * 执行任务
         * @param task
         */
        public void execute(Runnable task) {
    
    
            executeThread = new Thread(() -> {
    
    
                // t是真正执行任务的线程
                Thread t = new Thread(task);
                t.setDaemon(true);
                t.start();
                try {
    
    
                    // 这里要join,不然守护线程还没开始运行就可能结束了,另外也便于超时结束
                    executeThread.join();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                isStop = true;
            });

            executeThread.start();
        }

        /**
         * 等待mills还未结束就终止线程
         * @param mills
         */
       public void shutdown(long mills) {
    
    

           long endMills = System.currentTimeMillis() + mills;

           long remainMills = mills;

           while (remainMills > 0 && !isStop) {
    
    
               try {
    
    
                   TimeUnit.MILLISECONDS.sleep(10);
               } catch (InterruptedException e) {
    
    
                   e.printStackTrace();
               }
               remainMills = endMills - System.currentTimeMillis();
           }
           // 中断线程,executeThread结束后,其创建的守护线程也会随之结束
           executeThread.interrupt();
       }

    }
}

Guess you like

Origin blog.csdn.net/u022812849/article/details/108709330