# 《并发设计模式》第14章-两阶段终止模式-线程还没执行完任务怎么就退出了?
作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)
沉淀,成长,突破,帮助他人,成就自我。
- 本章难度:★★☆☆☆
- 本章重点:以场景故事的形式介绍线程未执行完任务就退出的案例,这也是大部分程序员经常会出现的问题,了解线程未执行完任务退出的场景,掌握线程退出的方法,并且能够结合自身实际项目思考哪些场景下使用了不当的方式退出了线程。
大家好,我是冰河~~
稍微了解并发编程,或者了解Thread类的小伙伴应该都比较了解:停止一个线程可以通过Thread的stop()方法和interrupt()方法,但是stop()方法会真的杀死线程,已经不建议使用stop()方法来停止线程,而interrupt()方法,则仅仅是通知线程要进行中断操作。但是,无论是哪个方法,在某些场景下,都会造成线程还没执行完任务,就会退出的问题。
# 一、故事背景
这天小菜接到一个开发监控报警系统中上传报警信息功能的任务,需求也比较简单,就是在监测到有异常告警时,将这些告警信息存入相应的队列中,由专门的线程来消费队列中的数据,并且将从队列中获取到的异常报警信息,上传到指定的服务器。开始小菜开发的还比较顺利。但是,在测试的过程中,发现消费队列中数据的线程,经常会出现还没有执行完队列中的任务,就会退出的现象。
为此,小菜翻来覆去的调试代码,思考怎么会出现这样的问题,在网上搜索对应的解决方案,最终,也没能解决问题。思来想去,还是决定向老王求助。
# 二、求助老王
小菜实在是不知道为什么线程没有执行完队列中的任务就会退出,于是,小菜离开自己的工位,来到老王身边:“老大,我遇到个问题,可以帮我看看吗?”。
“什么问题?”。
“就是我在开发监控报警系统中上传告警信息的功能时,消费队列中告警数据的线程在没有执行完队列中的任务时,经常会退出,造成报警信息丢失了”。
“是吗?我看看”。
于是,老王和小菜回到了小菜的工位上。
# 三、一眼定乾坤
老王来到小菜的工位,打开小菜写的代码,只是瞄了那么一眼,说道:“你写的代码确实有问题,你在程序中直接使用Thread类的interrupt()方法来中断线程,没有考虑线程还在执行任务的情况呀!”。
“额,我不是用了while(true)死循环来消费数据吗?正常情况下应该不会退出吧?”,小菜说道。
“有问题的,你直接使用Thread类的interrupt()方法来中断线程,尽管使用了while(true)循环,但是还是会造成线程没有执行完任务就退出的问题”,说着,老王基于小菜的代码,为小菜写了一段AlarmTest类的代码,来重现小菜的问题。
AlarmTest类的源码详见:concurrent-design-patterns-two-phase-termination工程下的io.binghe.concurrent.design.two.phase.alarm.wrong.AlarmTest。
public class AlarmTest {
public static void main(String[] args){
//向队列中添加数据
try{
for (int i = 1; i <= 100; i++){
AlarmQueue.getInstance().put(new AlarmInfo(String.valueOf(i), AlarmType.FAULT, String.valueOf(i).concat("出故障了")));
}
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("初始任务时未完成的任务数量: " + AlarmQueue.getInstance().size());
Thread executeTaskThread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
while (true) {
if (currentThread.isInterrupted()) {
System.out.println("线程被中断时未完成的任务数量: " + AlarmQueue.getInstance().size());
break;
}
try {
AlarmInfo alarmInfo = AlarmQueue.getInstance().take();
System.out.println("上报告警信息: " + alarmInfo);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
currentThread.interrupt();
}
}
});
executeTaskThread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
executeTaskThread.interrupt();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
“这个类跟你写的代码逻辑相似吧?”,老王问小菜。
“是的,大体逻辑差不多”,小菜回应道。
“我们来运行下这个类”,说着老王便开始运行这个AlarmTest类,运行完毕命令行输出了如下信息。
# 查看全文
加入冰河技术 (opens new window)知识星球,解锁完整技术文章与完整代码