本文为博主原创文章,请遵守文章最后的版权申明。
由于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
好啦,休息时间到。我们有空再会!