多线程 - 如何正确的终止线程?interrupt()、interrupted()、isInterrupted()区别?

总结

java线程之间是协同式,不是抢占式

  

因为是协同式,所以线程之间都是“商量”着来,最佳实践是没有谁强迫谁终止的情况的,因此才会淘汰stop()方法,详见 多线程 - 为何要弃用stop() suspend()?(不过你硬要调用stop()就另当别论...)

  • public void interrupt()  会为指定线程设置一个interrupted状态 -- 和stop()不同,不会强制线程停止,只会设置一个“interrupt标志位”,置为true(连续调用还是true,不会有异常发生。对比 Thread.start(), 多次调用会出现java.lang.IllegalStateException)。代表着“该线程被建议终止”。但是是否理会该建议,完全看该线程的具体逻辑是否去检查这个标志位,然后根据标志做出不同的反应。也有可能该线程的业务逻辑完全不理会标志位的变化,也就会继续我行我素的运行。
  • public static boolean interrupted() 会判断当前线程的interrupted状态,并且会重置该状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。之后,该标志位会被清除
  • public boolean isInterrupted() 会判断指定线程的interrupted状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。但是,该函数不会清除标志位。

 

    @Test
    public void testInterrupted(){
        boolean b1 = Thread.interrupted();  //false
        Thread.currentThread().interrupt();
        boolean b2 = Thread.interrupted();  //true
        boolean b3 = Thread.interrupted();  //false
    }

    @Test
    public void testIsInterrupted(){
        boolean b4 = Thread.currentThread().isInterrupted(); //false
        Thread.currentThread().interrupt();
        boolean b5 = Thread.currentThread().isInterrupted(); //true
        boolean b6 = Thread.currentThread().isInterrupted(); //true
    }

  

 

调用interrupt()抛出异常的情况(必看)

对于interrupt()方法,根据官方文档:

  • 如果指定线程调用了interrupt(),并且该线程不处于被sleep,wait,join的状态,那么一切正常,不会抛出调用InterruptException异常,就不会清除中断标志位。如果此时再调用isInterrupted()就会返回true。
  • 如果指定线程调用了interrupt()时,而指定线程正处于阻塞状态sleep,wait,join...线程自己就会立即抛InterruptException异常。
    • 原本“interrupt标志位”应该是true(因为之前调用了interrupt()),也会被清除变为false。如果再调用isInterrupted()返回false。
    • 如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态;如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
    • 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。

 

Q:为什么当指定线程在被阻塞状态(sleep, wait)时,调用interrupt(),要立即抛出InterruptException异常,并清除“interrupt标志位”(从true变为false)

A:【本答案不是很严谨,请大家斧正】线程在被阻塞过程中,可能掌握着某种资源。如果线程刚从阻塞回来,“interrupt标志位”就是true,很可能(视用户自定义的代码而定)线程就真准备中断,没有给程序任何回收资源的时间。因此先取消标志位,回收下资源然后视情况再决定是否真的interrupt。

 

如果中断sleep,wait,join等,就会抛InterruptException异常,就会清除中断标志位,那么这种情况应该怎么处理呢?为了保证数据的一致性和完整性,我们需要用Thread.interrupt()方法再次中断自己,置上中断标志位。例子如下:

    @Test
    public void testInterrupt() throws InterruptedException {
        Thread t1 = new Thread() {
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Interruted!");
                        break;
                    }
                    try {
                        Thread.sleep(2000); // 睡眠时中断会清除中断标志位
                    } catch (InterruptedException e) {
                        // 如果少了下面这句,这个线程虽然在外面中断,但是只要中断睡眠中的进程
                        // 就会清除中断标志位,仍然处于无限循环,会竞争CPU资源
                        Thread.currentThread().interrupt(); // 再次中断置上中断标记
                    }
                    Thread.yield();
                }
            }
        };
        t1.start();       //t1 thread would running
        Thread.sleep(200);//sleep main thread
        t1.interrupt();   //after main thread wake up, would plan to interrupt t1 thread
    }

  

 

interrupt()官方文档

中断线程。

如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。

如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。

如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。

如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。

如果以前的条件都没有保存,则该线程的中断状态将被设置。

中断一个不处于活动状态的线程不需要任何作用。

抛出:

SecurityException - 如果当前线程无法修改该线程

  

 

posted on 2020-03-07 21:52  frank_cui  阅读(841)  评论(0编辑  收藏  举报

导航

levels of contents