线程中断

Posted joe-go

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程中断相关的知识,希望对你有一定的参考价值。

Java中,线程中断是一种重要的线程协作机制,从表面上来看,中断就是让目标线程停止执行的意思,实际上却并非如此。

严格上讲,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了。至于目标线程接到通知后如何处理,则完全由目标线程自行决定。这点很重要,如果中断后,线程立即无条件退出,这种方式太暴力了,可能会引起一些数据不一致的问题,线程的stop()方法就是因为这个原因被弃用。

与线程中断有关的方法有三个:

1.interrupt():

先看源码:

public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

从注释就可以明显看出,interrupt()方法的作用就是“Just to set the interrupt flag”,即仅仅设置中断标识位。

2.isInterrupted():

先看源码:

public boolean isInterrupted() {
        return isInterrupted(false);
    }

这个方法通过检查中断标志位,判断当前线程是否被中断。

3.interrupted():

先看源码:

public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

这是一个静态方法,也是用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位状态。也就是说,连续调用该方法两次,那么第二次的返回值一定是false。

下面举例说明这三个方法:

public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(){
            @Override
            public void run(){
                while(true){
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        t1.interrupt();
    }

上面这个例子,虽然对t1进行了中断,但是运行后发现,线程t1并没有中断,由此可见,即使线程被设置上了中断状态,但是这个中断不会发生任何作用。

如果希望t1在中断后退出,就必须为它增加相应的中断处理代码,将上述例子修改为:

  

public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(){
            @Override
            public void run(){
                while(true){
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("t1 Interrupted!");
                        break;
                    }
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        t1.interrupt();
    }

输出结果:

t1 Interrupted!

表示线程中断成功。上述例子增加的部分 Thread.currentThread().isInterrupted() 判断当前线程是否被中断了,如果是,就退出循环体,结束线程。

思考:

存在这样的情况,当线程被阻塞的时候,比如被Object.wait()、Thread.join()或者Thread.sleep()这三种之一的方法阻塞时,该怎么中断线程呢?

有一种情况就是,线程在阻塞前恰好给自己设置了中断标识位,即调用了interrupt()方法,但是线程都阻塞了,当然也就不能通过 isInterrupt()方法来自己中断了;如果中断表示位都没有,就更不可能自己中断了。那么应该怎么办呢?

方法当然是有的,下面以sleep()方法为例,先来看看sleep()方法:

public static native void sleep(long millis) throws InterruptedException;

Thread.sleep()方法会让目标线程休眠若干时间,它会抛出一个 InterruptedException 中断异常。这个异常不是运行时异常,也就是说程序必须捕获并且处理它,当线程在sleep()时,如果被中断,这个异常就产生了。

看下面的例子:

public static void main(String[] args) throws InterruptedException {
        //将线程取名为t1
        Thread t1 = new Thread("t1"){
            @Override
            public void run(){
                while(true){
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("t1 Interrupted!");
                        break;
                    }
                    try {
                        System.out.println(Thread.currentThread().getName());//打印出当前线程的名字
                        System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位
                        System.out.println("开始睡觉");
                        Thread.sleep(2000);
                        System.out.println("睡觉结束");
                    } catch (InterruptedException e) {
                        System.out.println("Interrupted When Sleep!");
                        System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位
                        //设置中断状态
                        Thread.currentThread().interrupt();
                    }
                    System.out.println("让出资源");
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName());//打印出当前线程的名字
        t1.interrupt();
        System.out.println("000000");
    }

输出结果:

t1
false
开始睡觉
main
000000
Interrupted When Sleep!
false
让出资源
t1 Interrupted!

从输出结果来看,线程t1 start后,是没有中断标识位的,t1线程sleep 2秒,主线程main sleep了 1 秒,当t1还在sleep的时候,main线程执行了 t1.sleep(),所以t1是在sleep的时候被中断,由此捕获到异常,并设置了中断标识位,最后由break 退出中断线程。

注意:

Thread.sleep()方法由于中断而抛出异常,此时,它会清除中断标记,由上面输出结果可以看出在打印出中断后,中断标记位为false,如果不加上中断标识位,线程就不会中断,因此在异常处理中,再次设置上中断标识位。

 

参考: 《Java高并发程序设计 葛一鸣 郭超  著

以上是关于线程中断的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的代码中断线程泄漏?

一文搞懂 Java 线程中断

线程池

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

您的应用已进入中断状态,但没有代码可显示,因为所有线程都在执行外部代码(通常是系统或框架代码)

Java 线程的中断机制