当前位置:网站首页>并发编程 — 如何中断/停止一个运行中的线程?
并发编程 — 如何中断/停止一个运行中的线程?
2022-07-05 06:40:00 【搬运Gong】
如何正确的中断或者停止一个正在运行的线程呢?这是一道非常经典的面试题,里面涵盖了不少的知识点,读完本文,带你彻底掌握正确中断、停止线程的方法。
什么是中断机制?
首先
一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运。
所以,Thread.stop,Thread.suspend,Thread.resume 都已经被废弃了。
其次
在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的协商机制--中断,也即中断标识协商机制。中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;
接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程请求这条线程中断。此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个中断标识位,用于表示线程是否被中断,该标识位为true表示中断,为false表示未中断;
通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
线程(Thread类)中断的 API 方法
既然有线程中断的业务场景,那么Java 的大牛们必然也想到了,并提供了相应的 API 方法来完成,下面一起来看一下都有哪些,它们又是如何实现的。
void interrupt()
实例方法,仅仅是设置线程的中断状态为 true,发起一个协商而不会立刻停止线程。
static boolean interrupted()
静态方法,Thread.interrupted();
判断线程是否被中断并清除当前中断状态。
这个方法做了两件事:
1. 返回当前线程的中断状态,测试当前线程是否已被中断
2.将当前线程的中断状态清零并重新设置为 false,清除线程的中断状态
此方法有点不好理解,如果连续两次调用此方法,则第二次调用将返回 false,因为连续调用两次的结果可能不一样
boolean isInterrupted()
实例方法
判断当前线程是否被中断(通过检查中断标志位)
如何停止中断运行中线程?
1. 通过 volatile 设置共享变量
public class InterruptedDemo {
/**
* 线程中断标志位
*/
static volatile boolean stopFlag = false;
public static void main(String[] args) {
new Thread(() -> {
while (true) {
if (stopFlag) {
System.out.println(Thread.currentThread().getName() + " 线程中断,停止运行!");
break;
}
System.out.println(Thread.currentThread().getName() + " 线程启动,开始搬砖...");
}
},"t1").start();
try { TimeUnit.MILLISECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }
// 主线程告知t1 线程,需要中断了
stopFlag = true;
}
}
2. AtomicBoolean 原子类
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args) {
new Thread(() -> {
while (true) {
if (atomicBoolean.get()) {
System.out.println(Thread.currentThread().getName() + " 线程中断,停止运行!");
break;
}
System.out.println(Thread.currentThread().getName() + " 线程启动,开始搬砖...");
}
},"t1").start();
try { TimeUnit.MILLISECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }
// 主线程告知t1 线程,需要中断了
atomicBoolean.set(true);
这个和上面的 volatile 一样,同样可以实现。
3. Thread API
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " 线程中断,停止运行!");
break;
}
System.out.println(Thread.currentThread().getName() + " 线程启动,开始搬砖...");
}
},"t1");
t1.start();
try { TimeUnit.MILLISECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }
// t2线程通知 t1 线程中断运行
new Thread(() -> t1.interrupt(),"t2").start();
}
4. interrupt()、isInterrupted()源码分析
调用了底层的 native 方法 interrupt0(),仅仅是设置了一个标志而已。
重置标志位为 false。
通过查看源码,得出总结如下:
当对一个线程,调用interrupt()时:
① 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。
所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。②如果线程处于被阻塞状态(例如处于sleep、wait、join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
下面通过一个简单 case 来演示一下得出的总结:
- 验证对线程发出中断命令interrupt后,不会立刻执行中断
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 200; i++) {
System.out.println("----> " + i);
}
// 此时,验证 t1 线程是否收到了中断请求后,立刻终止执行了此处的输出?
System.out.println("t1线程的中断标志位02 --> " + Thread.currentThread().isInterrupted()); // true
},"t1");
t1.start();
System.out.println("t1线程的默认中断标志位--> " + t1.isInterrupted()); // false
try { TimeUnit.MILLISECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }
// 发送线程中断请求
t1.interrupt();
System.out.println("t1线程的中断标志位01 --> " + t1.isInterrupted()); // true
try { TimeUnit.MILLISECONDS.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); }
// 此时,t1 线程已经结束,回归默认状态
System.out.println("t1线程的中断标志位03 --> " + t1.isInterrupted()); // false
}
t1 线程执行完毕后,通过控制台的输出第二次获取线程的中断标识位,也时间 true。
当我们主线程休眠 500 ms 后,t1 线程已经执行完毕,生命周期结束,此时第三次获取 t1 线程的中断标识位,又变为了 false。验证了我们得出的结论:对于不活动的线程来说,中断请求默认为 false.
- 验证阻塞状态下,调用中断方法抛出异常
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " 线程中断!!");
break;
}
System.out.println("----> validate InterruptedException..");
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
// 抛出异常后,会将线程的中断标识清除,重置为默认状态,
// 所以,如果不加这一行代码的话,会进行死循环,其他线程对 t1 线程发出的中断请求失败
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}, "t1");
t1.start();
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
// 发送线程中断请求
t1.interrupt();
}
- static boolean interrupted() 静态方法说明
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " 线程中断状态01 ----> " + Thread.currentThread().isInterrupted());
/**
* Thread.interrupted() 静态方法,做了两件事
* 1. 返回当前线程的中断状态,测试当前线程是否已被中断
* 2.将当前线程的中断状态清零并重新设置为 false,清除线程的中断状态
*/
Thread.currentThread().interrupt();
System.out.println("====== 发送中断请求 =======");
System.out.println(Thread.currentThread().getName() + " 线程中断状态02 ----> " + Thread.currentThread().isInterrupted());
System.out.println(Thread.currentThread().getName() + " 线程中断状态03 ----> " + Thread.interrupted());
System.out.println(Thread.currentThread().getName() + " 线程中断状态04 ----> " + Thread.currentThread().isInterrupted());
}
来看一下源码:
可以看出,静态方法和实例方法,其实都是调用了同一个 native 方法,只不过传入的参数不一样,一个是需要重置状态,一个是不需要。
相信通过上面的这些内容,童鞋们对中断线程都有了一些更深层次的认识了吧!
边栏推荐
- 全局变量和静态变量的初始化
- kata container
- Spinningup drawing curve
- CGroup CPU group source code analysis
- Adg5412fbruz-rl7 applies dual power analog switch and multiplexer IC
- Marvell 88e1515 PHY loopback mode test
- 6-2 sequence table operation set
- Xiaomi written test real question 1
- Financial risk control practice -- feature derivation based on time series
- H5内嵌App适配暗黑模式
猜你喜欢
使用paping工具进行tcp端口连通性检测
Financial risk control practice -- feature derivation based on time series
PHY驱动调试之 --- MDIO/MDC接口22号和45号条款(一)
ROS2——工作空间(五)
Orin installs CUDA environment
数据库Mysql全部
C语言数组专题训练
Stack acwing 3302 Expression evaluation
[QT] QT multithreading development qthread
'mongoexport 'is not an internal or external command, nor is it a runnable program or batch file.
随机推荐
LSA Type Explanation - lsa-5 (type 5 LSA - autonomous system external LSA) and lsa-4 (type 4 LSA - ASBR summary LSA) explanation
Knapsack problem acwing 9 Group knapsack problem
. Net core stepping on the pit practice
Redis-02. Redis command
解读最早的草图-图像翻译工作SketchyGAN
Vant weapp swippecell set multiple buttons
Using handler in a new thread
2. Addition and management of Oracle data files
ROS2——功能包(六)
Rehabilitation type force deduction brush question notes D1
Xiaomi written test real question 1
Markdown syntax
mingling
Qt项目中的日志库log4qt使用
Vscode editor
Vant Weapp SwipeCell設置多個按鈕
Some classic recursion problems
在本地搭建一个微服务集群环境,学习自动化部署
How to correctly ask questions in CSDN Q & A
Xavier CPU & GPU 高负载功耗测试