Bug辉的博客 不忘初心,方得始终!

InterruptedException与线程的isInterrupted方法的不同之处

2017-10-06
Bug辉 [原创文章]

本文为博主原创文章,请遵守文章最后的版权申明。


由于Web开发工作中我们通常不会自己去创建、销毁线程,所以时间长了之后,我们往往会忽视一些司空见惯却又非常基础的操作的内涵。所以本文将介绍一个被众多小伙伴们忽视的基本操作——终止线程——InterruptedException与线程的isInterrupted()方法的不同之处。

Hi~ o(* ̄▽ ̄*)ブ 各位小伙伴们,大家好!由于本人的工作变动,所以有一段时间没有冒泡了,所以今天出来冒个泡。o(∩_∩)o 哈哈。不过现在时间有点晚了,女朋友催着我要休息了(单身汪请忽略这段话)。ㄟ(▔ ,▔)ㄏ,所以我就先挑个简短点的话题来聊聊。。。

好了,废话我就不扯了,来看代码:

工具方法

先来个工具方法,后续代码用到的都是同一个工具方法。

private static void log(String message){
    System.out.println(System.currentTimeMillis()
            + " [" + Thread.currentThread().getName()
            + "] " + message);
}

异常方式中断线程

先来看看通过InterruptedException终止线程的方式。

Thread.currentThread().setName("线程A");
log("开始执行");

Thread anotherThread = new Thread(() -> {
    log("开始执行");
    try {
        log("开始休眠");
        Thread.sleep(5000);
        log("结束休眠");
    } catch (InterruptedException e) {
        log("被中断啦!");
    }
    log("退出");
});
anotherThread.setName("线程B");
anotherThread.start();

//  做一些耗时的操作
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

anotherThread.interrupt();

log("退出");

执行结果:

1507305394681 [线程A] 开始执行
1507305394715 [线程B] 开始执行
1507305394715 [线程B] 开始休眠
1507305396715 [线程A] 退出
1507305396715 [线程B] 被中断啦!
1507305396715 [线程B] 退出

以上代码应该没啥问题吧,这是简单代码,应该很容易懂。

自己检查状态的方式中断线程

错误的方式

首先来看一个反例,估计有些小伙伴会像下面的代码一样去试图终止线程。

Thread.currentThread().setName("线程A");
log("开始执行");

Thread anotherThread = new Thread(() -> {
    log("开始执行");

    //  耗时操作开始
    StringBuilder sb = new StringBuilder();
    sb.append(UUID.randomUUID().toString());
    for (int i = 0; i < 99999; i++) {
        sb.replace(0, 1, UUID.randomUUID().toString());
    }
    //  耗时操作结束

    log("退出");
});
anotherThread.setName("线程B");
anotherThread.start();

//  做一些耗时的操作
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

anotherThread.interrupt();

log("退出");

运行结果:

1507305664020 [线程A] 开始执行
1507305664058 [线程B] 开始执行
1507305666058 [线程A] 退出
1507305674220 [线程B] 退出

很明显,上面代码的anotherThread.interrupt()完全没起作用,线程B 还是等到运行完了整个方法才退出。由于线程B跑着耗时的操作,所以很明显,在线程A退出了很久之后线程B才退出。

正确的方式

再来看看正确的方法

Thread.currentThread().setName("线程A");
log("开始执行");

Thread anotherThread = new Thread(() -> {
    log("开始执行");

    //  耗时操作开始
    StringBuilder sb = new StringBuilder();
    sb.append(UUID.randomUUID().toString());
    for (int i = 0; i < 99999; i++) {
        if (Thread.currentThread().isInterrupted()){
            break;
        }
        sb.replace(0, 1, UUID.randomUUID().toString());
    }
    //  耗时操作结束

    log("退出");
});
anotherThread.setName("线程B");
anotherThread.start();

//  做一些耗时的操作
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

anotherThread.interrupt();

log("退出");

运行结果:

1507305936144 [线程A] 开始执行
1507305936186 [线程B] 开始执行
1507305938186 [线程A] 退出
1507305938186 [线程B] 退出

很明显,这次线程B几乎就是在线程A调用了anotherThread.interrupt()之后就退出了。输出的日志中第一列是时间戳,如果没看明白,可以仔细看看两个线程退出的时间间隔。

结论

InterruptedException的方式用于将处于阻塞状态的线程终止。interrupt()方法用于将正在执行不阻塞的操作的线程“终止”,不过这个时候需要被终止的线程自己去轮询中断标记。

也就是说,线程会终止就两种情况:

  • 正在执行可能会抛出InterruptedException的阻塞操作
  • 调用Thread.currentThread().isInterrupted()通过轮询的方式自己检查当前线程是否被设置了中断标记。这种情况下的终止其实与线程正常执行完过程退出几乎一样,只是在出现中断标记时通过流程控制语句跳过了一些业务逻辑而已。

一些需要注意的地方

另外还有一个小细节想提醒小伙伴们。IO阻塞或者因synchronized产生的同步阻塞是没有办法通过InterruptedException来中断的!o(∩_∩)o

好啦,休息时间到。我们有空再会!


版权声明

知识共享许可协议
若无特殊说明则文章内容均为博主原创内容,包括但不限于代码、文字、图片。
《InterruptedException与线程的isInterrupted方法的不同之处》 Bug辉 采用 知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议 进行许可。
转载文章时文章署名请注明原作者为Bug辉(Elvin Zeng、zenghui、曾辉也行)并带上原文链接:https://www.bughui.com/2017/10/06/difference-between-InterruptedException-and-isInterrupted/

鉴于目前国人版权意识比较薄弱,所以路过的同学可能并不了解CC协议。在此特地介绍一下!上述协议大概的意思是(这里只是简单解释,并不替代协议,以上述协议为准):
  • 权利:遵守协议的情况下你可以在任何媒介以任何形式复制、发行本作品。
  • 约束:使用本作品得保留署名(作者+原文链接),不得声称或暗示文章是你创作的。
  • 约束:你不可以将本作品用于商业目的。
  • 约束:如果你再混合、转换、或者基于本作品创作,你不可以发布修改后的作品。
  • 在得到作者允许的情况下你可以不用受上述条款约束。
不论本作品是否对你有益,不论你是否认同本作品的观点,本作品都是作者的劳动产物。尊重别人的劳动别人才会尊重你的劳动是吧!


类似文章

评论

打赏时请在备注信息中填上你的称呼!好让我把你的名字加入致谢名单