专栏首页Java架构沉思录必须要掌握的 InterruptedException 异常处理

必须要掌握的 InterruptedException 异常处理

优质文章,及时送达

InterruptedException异常

在了解InterruptedException异常之前应该了解以下的几个关于线程的一些基础知识。而且得知道什么时候会抛InterruptedException异常

当阻塞方法收到中断请求的时候就会抛出InterruptedException异常

线程的状态

线程在一定的条件下会发生状态的改变,下面是线程的一些状态

  • 初始(NEW):新建一个线程的对象,还未调用start方法
  • 运行(RUNNABLE):java线程中将已经准备就绪(Ready)和正在运行中(Running)的两种状态都统称为“Runnable”。准备就绪的线程会被放在线程池中等待被调用
  • 阻塞(BLOCKED):是因为某种的原因而放弃了CPU的使用权,暂时的停止了运行。直到线程进入准备就绪(Ready)状态才会有机会转到运行状态
  • 等待(WAITING):该状态的线程需要等待其他线程做出一些特定的动作(通知或者是中断)
  • 超时等待(TIME_WAITING):该状态和上面的等待不同,他可以在指定的时间内自行返回
  • 终止(TERMINATED):线程任务执行完毕

而InterruptedException异常从字面意思上就是中断异常,那么什么是中断呢?学习中断之前我们先了解一下具体什么是阻塞

线程阻塞

线程阻塞通常是指一个线程在执行过程中暂停,以等待某个条件的触发。而什么情况才会使得线程进入阻塞的状态呢?

  • 等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒
  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中
  • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态

线程中断

如果我们有一个运行中的软件,例如是杀毒软件正在全盘查杀病毒,此时我们不想让他杀毒,这时候点击取消,那么就是正在中断一个运行的线程。

每一个线程都有一个boolean类型的标志,此标志意思是当前的请求是否请求中断,默认为false。当一个线程A调用了线程B的interrupt方法时,那么线程B的是否请求的中断标志变为true。而线程B可以调用方法检测到此标志的变化。

  • 阻塞方法:如果线程B调用了阻塞方法,如果是否请求中断标志变为了true,那么它会抛出InterruptedException异常。抛出异常的同时它会将线程B的是否请求中断标志置为false
  • 非阻塞方法:可以通过线程B的isInterrupted方法进行检测是否请求中断标志为true还是false,另外还有一个静态的方法interrupted方法也可以检测标志。但是静态方法它检测完以后会自动的将是否请求中断标志位置为false。例如线程A调用了线程B的interrupt的方法,那么如果此时线程B中用静态interrupted方法进行检测标志位的变化的话,那么第一次为true,第二次就为false。下面为具体的例子:
/**
 * @program: Test
 * @description:
 * @author: hu_pf@suixingpay.com
 * @create: 2018-07-31 15:43
 **/
public class InterrupTest implements Runnable{

    public void run(){
            try {
                while (true) {
                    Boolean a = Thread.currentThread().isInterrupted();
                    System.out.println("in run() - about to sleep for 20 seconds-------" + a);
                    Thread.sleep(20000);
                    System.out.println("in run() - woke up");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();//如果不加上这一句,那么cd将会都是false,因为在捕捉到InterruptedException异常的时候就会自动的中断标志置为了false
                Boolean c=Thread.interrupted();
                Boolean d=Thread.interrupted();
                System.out.println("c="+c);
                System.out.println("d="+d);
            }
    }
    public static void main(String[] args) {
        InterrupTest si = new InterrupTest();
        Thread t = new Thread(si);
        t.start();
        //主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
        try {
            Thread.sleep(2000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("in main() - interrupting other thread");
        //中断线程t
        t.interrupt();
        System.out.println("in main() - leaving");
    }
}

打印的参数如下:

in run() - about to sleep for 20 seconds-------false
in main() - interrupting other thread
in main() - leaving
c=true
d=false

现在知道线程可以检测到自身的标志位的变化,但是他只是一个标志,如果线程本身不处理的话,那么程序还是会执行下去,就好比,老师在学校叮嘱要好好学习,具体什么时候,如何好好学习还是看自身。

因此interrupt() 方法并不能立即中断线程,该方法仅仅告诉线程外部已经有中断请求,至于是否中断还取决于线程自己

InterruptedException异常的处理

简单的了解了什么是阻塞和中断以后,我们就该了解碰到InterruptedException异常该如何处理了。

不要不管不顾

有时候阻塞的方法抛出InterruptedException异常并不合适,例如在Runnable中调用了可中断的方法,因为你的程序是实现了Runnable接口,然后在重写Runnable接口的run方法的时候,那么子类抛出的异常要小于等于父类的异常。

而在Runnable中run方法是没有抛异常的。所以此时是不能抛出InterruptedException异常。如果此时你只是记录日志的话,那么就是一个不负责任的做法,因为在捕获InterruptedException异常的时候自动的将是否请求中断标志置为了false。

至少在捕获了InterruptedException异常之后,如果你什么也不想做,那么就将标志重新置为true,以便栈中更高层的代码能知道中断,并且对中断作出响应。

捕获到InterruptedException异常后恢复中断状态

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

参考文章

https://blog.csdn.net/sinat_34596644/article/details/51405327 https://blog.csdn.net/u011784767/article/details/51428101 https://www.ibm.com/developerworks/cn/java/j-jtp05236.html https://blog.csdn.net/pange1991/article/details/53860651

本文分享自微信公众号 - Java架构沉思录(code-thinker),作者:黄哥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java线程为何没有Running状态?我猜你不知道。

    Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程状态是两个不同层面的事。具体而言,这里说的 Java 线程状态均来自于 Thread 类下的 Stat...

    黄泽杰
  • 说说Netty的线程模型

    最近发现极客时间的很多课程中,都穿插到了 Netty,可见 Netty 的重要性。基于此,给大家推荐一下这篇文章!

    黄泽杰
  • 一文搞定Java并发编程面试考点

    任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(bool on);true则把该线程设置为守护线程,反之则为用户线程。Thre...

    黄泽杰
  • CAS机制是什么?

    因为多核CPU的存在,总是需要保障程序高性能的利用计算机的CPU资源,并要保障计算结果与预期的一致。因此常用的方式就是加锁方式。就是占用->占用结束->释放的...

    程序员_备忘录
  • PHP到底能不能实现多线程?

    前些天帮同事查一个问题,第一次接触到了 PHP 的多线程,原以为 PHP 普遍都是单线程模型,并不适合多线程领域,花些时间翻了几个多线程的项目源码之后,发现 P...

    A梦多啦A
  • 线程的基本状态

    1、新建状态(New):新创建了一个线程对象。 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可...

    shimeath
  • MySQL如何管理客户端连接?线程池篇

    在之前的文章里,为大家介绍了MySQL的连接管理线程的工作方式,在这一篇里为大家介绍管理连接的第二种方式,线程池。

    MySQLSE
  • 【Java】14 多线程

    并发:在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的...

    Demo_Null
  • 线程的调度方式——抢占式、非抢占式

    在抢占模式下,操作系统负责分配CPU时间给各个进程,一旦当前的进程使用完分配给自己的CPU时间,操作系统将决定下一个占用CPU时间的是哪一个线程。因此操作系统...

    MickyInvQ
  • 一次线程池引发的线上故障分析

    线上监控到大量接口报错,定位到异常机器,将异常机器隔离后,线上服务恢复正常。拿到业务报错日志如下:

    有赞coder

扫码关注云+社区

领取腾讯云代金券