线程中断

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

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

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

1.interrupt():

先看源码:

 

 1 public void interrupt() {
 2         if (this != Thread.currentThread())
 3             checkAccess();
 4 
 5         synchronized (blockerLock) {
 6             Interruptible b = blocker;
 7             if (b != null) {
 8                 interrupt0();           // Just to set the interrupt flag
 9                 b.interrupt(this);
10                 return;
11             }
12         }
13         interrupt0();
14     }

 

 

 

从注释就可以明显看出,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在中断后退出,就必须为它增加相应的中断处理代码,将上述例子修改为:

 1 public static void main(String[] args) throws InterruptedException {
 2         Thread t1 = new Thread(){
 3             @Override
 4             public void run(){
 5                 while(true){
 6                     if (Thread.currentThread().isInterrupted()) {
 7                         System.out.println("t1 Interrupted!");
 8                         break;
 9                     }
10                     Thread.yield();
11                 }
12             }
13         };
14         t1.start();
15         Thread.sleep(1000);
16         t1.interrupt();
17     }

 

输出结果:

t1 Interrupted!

表示线程中断成功。上述例子增加的部分第6行 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()时,如果被中断,这个异常就产生了。

看下面的例子:

 1 public static void main(String[] args) throws InterruptedException {
 2         //将线程取名为t1
 3         Thread t1 = new Thread("t1"){
 4             @Override
 5             public void run(){
 6                 while(true){
 7                     if (Thread.currentThread().isInterrupted()) {
 8                         System.out.println("t1 Interrupted!");
 9                         break;
10                     }
11                     try {
12                         System.out.println(Thread.currentThread().getName());//打印出当前线程的名字
13                         System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位
14                         System.out.println("开始睡觉");
15                         Thread.sleep(2000);
16                         System.out.println("睡觉结束");
17                     } catch (InterruptedException e) {
18                         System.out.println("Interrupted When Sleep!");
19                         System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位
20                         //设置中断状态
21                         Thread.currentThread().interrupt();
22                     }
23                     System.out.println("让出资源");
24                     Thread.yield();
25                 }
26             }
27         };
28         t1.start();
29         Thread.sleep(1000);
30         System.out.println(Thread.currentThread().getName());//打印出当前线程的名字
31         t1.interrupt();
32         System.out.println("000000");
33     }

 

输出结果:

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高并发程序设计 葛一鸣 郭超  著

posted on 2018-09-18 17:35  AoTuDeMan  阅读(286)  评论(0编辑  收藏  举报

导航