2.Java怎么正确停止线程呢?
创始人
2024-01-30 20:12:49

目录

需求:

怎么停止呢?

再说interrrupt:

为什么 Java 不提供强制停止线程的能力呢?

如何用 interrupt 停止线程呢?

sleep 期间能否感受到中断呢?

正确停止代码

1、方法签名中抛出异常

2、再次中断


一般我们在开发中想要启动线程需要调用 Thread 类的 start() 方法,并在 run() 方法中定义需要执行的任务就好了。

那么停止就不是那么好玩了,下面就总结瞎怎么停止线程。

需求:

通常情况下,我们不会手动停止一个线程,而是允许线程运行到结束,然后让它自然停止。

但是依然会有许多特殊的情况需要我们提前停止线程,比如:用户突然关闭程序,或程序运行出错重启等。

在这种情况下,即将停止的线程在很多业务场景下仍然很有价值。尤其是我们想写一个健壮性很好,能够安全应对各种场景的程序时,正确停止线程就显得格外重要。

但是Java 并没有提供简单易用,能够直接安全停止线程的能力。

所以搞多线程开发,对我们程序员来说是考验技术得时候了。

怎么停止呢?

先说结论:对于 Java 而言,最正确的停止线程的方式是使用 interrupt。

再说interrrupt:

interrupt 仅仅起到通知被停止线程的作用。

而对于被停止的线程而言,它拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。

这个时候又有问题了。

为什么 Java 不提供强制停止线程的能力呢?

因为Java 希望程序间能够相互通知、相互协作地管理线程,因为如果不了解对方正在做的工作,贸然强制停止线程就可能会造成一些安全的问题,为了避免造成问题就需要给对方一定的时间来整理收尾工作。

比如:

线程正在写入一个文件,这时收到终止信号,它就需要根据自身业务判断,是选择立即停止,还是将整个文件写入成功后停止,而如果选择立即停止就可能造成数据不完整,不管是中断命令发起者,还是接收者都不希望数据出现问题。

如何用 interrupt 停止线程呢?

源码:

while (!Thread.currentThread().isInterrupted() && more work to do) {do more work
}

可以看到在 while 循环体判断语句中,首先通过 Thread.currentThread().isInterrupt() 判断线程是否被中断,随后检查是否还有工作要做。

&& 逻辑表示只有当两个判断条件同时满足的情况下,才会去执行下面的工作。

具体例子:

public class StopThread implements Runnable {@Overridepublic void run() {int count = 0;while (!Thread.currentThread().isInterrupted() && count < 1000) {System.out.println("count = " + count++);}}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new StopThread());thread.start();Thread.sleep(5);thread.interrupt();}
}
  1. 首先判断线程是否被中断,然后判断 count 值是否小于 1000。
  2. 这个线程的工作内容很简单,就是打印 0~999 的数字,每打印一个数字 count 值加 1,可以看到,线程会在每次循环开始之前,检查是否被中断了。

  3. 接下来在 main 函数中会启动该线程,然后休眠 5 毫秒后立刻中断线程,该线程会检测到中断信号,于是在还没打印完1000个数的时候就会停下来,这种就属于通过 interrupt 正确停止线程的情况。

sleep 期间能否感受到中断呢?

先说结论:线程还在休眠,仍然能够响应中断通知,并抛出异常。

案例:

    public static void main(String[] args) throws InterruptedException {Runnable runnable = () -> {int num = 0;try {while (!Thread.currentThread().isInterrupted() && num <= 1000) {System.out.println(num);num++;Thread.sleep(1000000);}} catch (InterruptedException e) {e.printStackTrace();}};Thread thread = new Thread(runnable);thread.start();Thread.sleep(5);thread.interrupt();}
}

代码意思是:

如果线程在执行任务期间有休眠需求,也就是每打印一个数字,就进入一次 sleep ,而此时将 Thread.sleep() 的休眠时间设置为 1000 秒钟。

主线程休眠 5 毫秒后,通知子线程中断,此时子线程仍在执行 sleep 语句,处于休眠中。

所以如果不能中断就会带来严重的问题,因为响应中断太不及时了。正因为如此,Java 设计者在设计之初就考虑到了这一点。

如果 sleep、wait 等可以让线程进入阻塞的方法使线程休眠了,而处于休眠中的线程被中断,那么线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。

java.lang.InterruptedException: sleep interruptedat java.base/java.lang.Thread.sleep(Native Method)at com.qax.tsgz.toolbox.input.domain.operation.impl.StopThread.lambda$main$0(StopThread.java:33)at java.base/java.lang.Thread.run(Thread.java:834)

正确停止代码

1、方法签名中抛出异常

先说一个反例:

在方法中使用 try/catch 或在方法签名中声明 throws  InterruptedException。

void subTas() {try {Thread.sleep(1000);} catch (InterruptedException e) {// 在这里不处理该异常是非常不好的}
}

如上面的代码所示,catch 语句块里代码是空的,它并没有进行任何处理。

假设线程执行到这个方法,并且正在 sleep,此时有线程发送 interrupt 通知试图中断线程,就会立即抛出异常,并清除中断信号。抛出的异常被 catch 语句块捕捉。

但是,捕捉到异常的 catch 没有进行任何处理逻辑,相当于把中断信号给隐藏了,这样做是非常不合理的。

正确方式:

方法签名中抛出异常。

void subTask2() throws InterruptedException {Thread.sleep(1000);
}

正如代码所示,要求每一个方法的调用方有义务去处理异常。

调用方要不使用 try/catch 并在 catch 中正确处理异常,要不将异常声明到方法签名中。

如果每层逻辑都遵守规范,便可以将中断信号层层传递到顶层,最终让 run() 方法可以捕获到异常。

而对于 run() 方法而言,它本身没有抛出 checkedException 的能力,只能通过 try/catch 来处理异常。层层传递异常的逻辑保障了异常不会被遗漏,而对 run() 方法而言,就可以根据不同的业务逻辑来进行相应的处理。

2、再次中断

在 catch 语句块中调用 Thread.currentThread().interrupt() 函数

private void reInterrupt() {try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}
}

因为如果线程在休眠期间被中断,那么会自动清除中断信号。如果这时手动添加中断信号,中断信号依然可以被捕捉到。

这样后续执行的方法依然可以检测到这里发生过中断,可以做出相应的处理,整个线程可以正常退出。

需要注意的是:

我们需要注意,我们在实际开发中不能盲目吞掉中断。

我们需要注意,我们在实际开发中不能盲目吞掉中断。

我们需要注意,我们在实际开发中不能盲目吞掉中断。

如果不在方法签名中声明,也不在 catch 语句块中再次恢复中断,而是在 catch 中不作处理,我们称这种行为是【屏蔽了中断请求】

如果我们盲目地屏蔽了中断请求,会导致中断信号被完全忽略,最终导致线程无法正确停止。

相关内容

热门资讯

长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
1秒多少毫秒 1秒等于多少毫秒... 长度单位换算1丈=10尺;1尺=10寸;1寸=10分;1分=10厘;1丈≈3.33米;1尺≈3.33...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
中国电信人工电话多少 中国电信... 当我们在使用中国移动、中国电信和中国联通提供的电信业务时,可能会发生争议。建议直接向服务提供商投诉,...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...