java中使用interrupt通知线程停止详析
作者:_灯火阑珊处 发布时间:2023-09-03 11:41:26
前言:
使用 interrupt 来通知线程停止运行,而不是强制停止!
普通情况停止线程
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是1W的倍数");
}
num++;
}
System.out.println("任务运行结束!");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
// 等待1s
Thread.sleep(1000);
// 通知停止线程
thread.interrupt();
}
}
使用 thread.interrupt() 通知线程停止
但是 线程需要配合:
在 while 中使用 Thread.currentThread().isInterrupted() 检测线程当前的状态
运行结果:
……
……
221730000是1W的倍数
221740000是1W的倍数
221750000是1W的倍数
221760000是1W的倍数
221770000是1W的倍数
221780000是1W的倍数
221790000是1W的倍数
221800000是1W的倍数
任务运行结束!Process finished with exit code 0
在可能被阻塞情况下停止线程
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 300 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
}
try {
// 等个1秒,模拟阻塞
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("线程已停止!!");
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 等待时间要小于上面设置的1秒,不然线程都运行结束了,才执行到下面的thread.interrupt();代码
Thread.sleep(500);
// 通知停止线程
thread.interrupt();
}
}
线程在sleep 1秒的过程中,收到interrupt信号被打断,
线程正在sleep过程中响应中断的方式就是抛出 InterruptedException 异常
运行结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
线程已停止!!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
at java.lang.Thread.run(Thread.java:748)Process finished with exit code 0
在每次迭代后都阻塞的情况下停止线程
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 5秒后通知停止线程
Thread.sleep(5000);
thread.interrupt();
}
}
当每次迭代都会让线程阻塞一段时间的时候,在 while/for 循环条件判断时,
是不需要使用 *Thread.currentThread().isInterrupted() *判断线程是否中断的
运行结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
如果将上述代码中的 try/catch 放在 while 循环内:
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
try {
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
// 5秒后通知停止线程
Thread.sleep(5000);
thread.interrupt();
}
}
运行结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数
700是100的倍数
……
……
会发现虽然抛出了异常,但是程序并没有停止,还在继续输出,
即使在 while 条件判断处添加 !Thread.currentThread().isInterrupted() 条件,依然不能停止程序!
原因是
java语言在设计 sleep() 函数时,有这样一个理念:
就是当它一旦响应中断,便会把 interrupt 标记位清除。
也就是说,虽然线程在 sleep 过程中收到了 interrupt 中断通知,并且也捕获到了异常、打印了异常信息,
但是由于 sleep 设计理念,导致 Thread.currentThread().isInterrupted() 标记位会被清除,
所以才会导致程序不能退出。
这里如果要停止线程,只需要在 catch 内 再调用一次 interrupt(); 方法
try {
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
所以说,不要以为调用了 interrupt() 方法,线程就一定会停止。
两种停止线程最佳方法
1. 捕获了 InterruptedException 之后的优先选择:在方法签名中抛出异常
public class RightWayStopThreadInProd implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
System.out.println("go...");
try {
throwInMethod();
} catch (InterruptedException e) {
// 捕获异常,进行保存日志、停止程序等操作
System.out.println("stop");
e.printStackTrace();
}
}
}
/**
* 如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch
* 这样调用方才能捕获异常并作出其它操作
* @throws InterruptedException
*/
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
}
如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是 try/catch
这样调用方才能捕获异常并做出其它操作。
2. 在 catch 中调用 Thread.currentThread().interrupt(); 来恢复设置中断状态
public class RightWayStopThreadInProd2 implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("程序运行结束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
这里的 if (Thread.currentThread().isInterrupted()) 判断,就是要你的代码有响应中断的能力。
总结
调用 interrupt 方法不一定会中断线程
通知线程停止,线程不会立即停止,而是会在合适的时候停止
代码要有响应中断的能力
来源:https://juejin.cn/post/7146124381147070495
猜你喜欢
- 一、封装类1.封装类概念Java中存在基础数据类型,但是在某些情况下,我们要对基础数据类型进行对象的操作,例如,集合中只能存对象,而不能存在
- 前言关于mybatis-plus的简介以及基本使用,我在《SpringBoot整合mybatis-plus–入门超详细》一文中已做介绍,此处
- 目录一:spring读取配置或注解的过程二:spring的bean的生命周期2.1:实例化 Instantiation2.2:初始化3: 使
- fopen(打开文件)相关函数 open,fclose表头文件 #include<stdio.h>定义函数 FILE * fop
- spring容器是负责实例化、配置、组装组件的容器。容器的配置有很多,常用的是xml、Java注解和Java代码。在spring中Ioc容器
- 通过http://localhost:7002/card/services/HelloWorld?wsdl访问到xml如下,说明接口写对了。
- 线程可以划分优先级,优先级高的线程得到的CPU资源比较多,也就是CPU优先执行优先级高的线程对象中的任务。设置线程优先级有助于帮助线程规划器
- 目录1、this代表了()的对象引用,super表示的是当前对象的()对象?2、输出内容是:3、下面程序的输出是:()4、执行下列代码的输出
- 迭代器模式,一直没用过,也不会用。恰巧MyBatis框架中也使用到了迭代器模式,而且看起来还比较简单,在以后的工作中,若有需要咱们可模仿它的
- 一、前言对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。二、MyBatis的初始化做了什么2.1 Mybatis的
- 一、ArrayList简介在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:ArrayList底层是一段连
- 前言1.Map里面只能存放对象,不能存放基本类型,例如int,需要使用Integer2.Map集合取出时,如果变量声明了类型,会先进行拆箱,
- 网上找了几个,写的都不太适合,有的写出来了,也没有给出参考的算法链接。这样就导致了如果产生错误我们无法排查(不懂原理怎么排查对吧)。如果在使
- 1.SQL注入:程序向后台数据库传递SQL时,用户提交的数据直接拼接到SQL语句中并执行,从而导入SQL注入攻击。字符型注入:黑色部分为拼接
- 实现二分法查找二分法查找,需要数组内是一个有序的序列二分查找比线性查找:数组的元素数越多,效率提高的越明显二分查找的效率表示:O(log2N
- 简介:本文已一个简要的代码示例介绍ThreadLocal类的基本使用方式,在此基础上结合图片阐述它的内部工作原理。早在JDK1.2的版本中就
- 1、LongAdder由来LongAdder类是JDK1.8新增的一个原子性操作类。AtomicLong通过CAS算法提供了非阻塞的原子性操
- 前言多数据源的事务处理是个老生常谈的话题,跨两个数据源的事务管理也算是分布式事务的范畴,在同一个JVM里处理多数据源的事务,比较经典的处理方
- instanceof关键字用于判断一个引用类型变量所指向的对象是否是一个类(或接口、抽象类、父类)的实例。 举个例子:public
- 一、前言环境:jdk 1.8,SpringCloud Greenwich.SR2。如题,springcloud config-client启