掘金 后端 ( ) • 2021-11-26 14:26

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

一、中断线程

线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,标记线程处于中断状态,但不会终止线程,线程还会继续执行。中断的结果线程是死亡,还是等待新的任务或是继续运行至下一步,取决于这个程序本身。线程会不时地检测这个中断标示位,以判断是否应该被中断(中断标示值是否为true)。


二、判断线程是否被中断

判断某个线程是否已被发送过中断请求,使用Thread.currentThread().isInterrupted()方法(因为它将线程中断标示位设置为true后,不会立刻清除中断标示位,即不会将中断标示设置为false),不要使用thread.interrupted()(检查当前线程是否已经中断,线程的中断状态由该方法清除,即将中断标示设置为false,如果连续两次调用该方法,则第二次调用将返回false,在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)


三、如何中断线程

如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait以及可中断的通道上的IO操作方法后可进入阻塞状态),则线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断,某些线程非常重要,以至于它们应该不理会中断,而是在处理完抛出的异常之后继续执行,但是普遍的情况是,一个线程将把中断看作一个终止请求。


四、中断应用

1)使用中断信号量中断非阻塞状态的线程
使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量,然后有秩序地中止任务。

public class ThreadExample {

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

        InterruptThreadBySemaphore interruptThreadBySemaphore = new InterruptThreadBySemaphore();
        interruptThreadBySemaphore.start();
        Thread.sleep(3000);
        System.out.println("asking thread to stop");
        //设置中断信号量
        interruptThreadBySemaphore.stop = true;
        Thread.sleep(3000);
        System.out.println("stopping application...");
    }
     static class InterruptThreadBySemaphore extends Thread {

         /**
          * 线程中断信号量
          */
         volatile boolean stop = false;

         @Override
         public void run() {
             super.run();
             //每隔一秒检测一下中断信号量
             while (!stop) {
                 System.out.println("thread is running....");
                 long time = System.currentTimeMillis();
                 /**
                  * 使用while循环模拟sleep方法,使用sleep,否则在阻塞时会抛
                  * InterruptedException异常而退出循环
                  */
                 while ((System.currentTimeMillis() - time) 

2)使用thread.interrupt()中断非阻塞状态线程

public class ThreadExample {

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

        InterruptThreadByInterrupt interruptThreadByInterrupt = new InterruptThreadByInterrupt();
        interruptThreadByInterrupt.start();
        Thread.sleep(3000);
        System.out.println("asking thread to stop");
        //发起中断请求
        interruptThreadByInterrupt.interrupt();
        Thread.sleep(3000);
        System.out.println("stopping application...");
    }

    static class InterruptThreadByInterrupt extends Thread {

        @Override
        public void run() {
            super.run();
            //每隔一秒检测一下中断信号量
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread is running....");
                long time = System.currentTimeMillis();
                /**
                 * 使用while循环模拟sleep方法,使用sleep,否则在阻塞时会抛
                 * InterruptedException异常而退出循环
                 */
                while ((System.currentTimeMillis() - time) 

3)使用thread.interrup()中断阻塞状态线程
Thread.interrupt()方法不会中断一个正在运行的线程。是设置线程的中断标示位,在线程受阻塞的地方(如sleep,wait,join等)抛出一个异常InterruptedException,并且中断状态也将被清除,这样线程就得以退出阻塞的状态。

public class ThreadExample {

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

        InterruptBlockingThread interruptBlockingThread = new InterruptBlockingThread();
        interruptBlockingThread.start();
        Thread.sleep(3000);
        System.out.println("asking thread to stop");
        interruptBlockingThread.interrupt();
        Thread.sleep(3000);
        System.out.println("stopping application...");
    }


    static class InterruptBlockingThread extends Thread {

        @Override
        public void run() {
            super.run();
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread is running....");
                try {
                    /**
                     * 如果线程阻塞,将不会去检查中断信号量stop,所以thread.interrupt()
                     * 会使用阻塞线程从阻塞的地方抛出异常,让阻塞线程从阻塞状态逃离出来,
                     * 并进行异常块进行,相应的处理
                     */
                    Thread.sleep(1000);//线程阻塞,如果线程收到中断操作信号将抛出异常
                }catch (InterruptedException e){
                    System.out.println("thread interrupted....");
                    /**
                     * 如果线程在调用Object.wait()方法,或者该类的join(),sleep()方法
                     * 过程中受阻,则其中断状态将被清除
                     */
                    System.out.println(this.isInterrupted());//false
                    /**
                     * 中不中断由自己决定,如果需要中断线程,则重新设置中断位,
                     * 如果不需要,则不用调用
                     */
                    Thread.currentThread().interrupt();
                }

            }
            System.out.println("thread exiting...");
        }
    }
}

运行结果:\

image.png

image.png


五、总结

Java的中断是一种协作机制。也就是说调用线程对象的interrupt()方法并不一定就中断了正在运行的线程,它只是要求线程在合适的时机中断自己。每个线程都有一个boolean的中断状态,interrupt()方法仅仅只是将该状态置为true,比如对正常运行的线程调用interrupt()并不能终止它,只是改变了interrupt标示符。
一般来说,如果一个方法声明抛出InterruptedException,表示该方法是可中断的,比如wait,sleep,join,也就是说可中断方法会对interrupt调用做出响应(例如sleep响应interrupt的操作包括清除中断状态,抛出InterruptedException),异常都是由可中断方法自己抛出来的,并不是直接由interrupt()方法直接引起的。
Object.wait(),Thread.sleep()方法,会不断的轮询监听interrupted标志位,发现其设置为true后,会停止阻塞并抛出InterruptedException异常。