zoukankan      html  css  js  c++  java
  • 线程中断

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

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    模拟信号、数字信号,信号是如何被处理的?
    模拟信号、数字信号,信号是如何被处理的?
    语气词、拟声词、动作与哭、量词、标点、特殊的符号
    语气词、拟声词、动作与哭、量词、标点、特殊的符号
    联合概率(joint probability)、分布函数(distribution function)
    联合概率(joint probability)、分布函数(distribution function)
    考古与历史
    考古与历史
    从贝叶斯模型(Bayes)到生成模型(Generative models)(生成式分类器,generative classifier)
    stm32开发笔记一:使用固件库在RealView-MDK中新建工程(上)
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/9670271.html
Copyright © 2011-2022 走看看