在Java中如何优雅的停止一个线程?可别再用Thread.stop()了!
写在开头
经过上几篇博文的学习,我们知道在Java中可以通过new Thread().start()创建一个线程,那今天我们就来思考另外一个问题:线程的终止
自然终止有两种情况:
1. 线程的任务执行完成;
2. 线程在执行任务过程中发生异常。
start之后,如果线程没有走到终止状态,我们该如何停止这个线程呢?
为什么stop终止不可用
翻看Thread源码后,发现其提供过一个stop()方法,可以用来终止线程,我们看一下它的源码。
【源码解析1】
@Deprecated
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
if (threadStatus != 0) {
resume(); // Wake up thread if it was suspended; no-op otherwise
}
// The VM can handle all thread states
stop0(new ThreadDeath());
}
这个方法使用了@Deprecated修饰,代表着它是废弃的方法,在Java的编码规约中,过时的方法不建议继续使用,并且在这个方法的注释中官方也提示说这是一个不安全的强制恶意中断方法,会破坏线程的原子性。

因此,在这里强烈建议大家不要再用stop方法去停止线程了!
如何优雅的停止一个线程
我们知道线程只有从 runnable 状态(可运行/运行状态) 才能进入terminated 状态(终止状态),如果线程处于 blocked、waiting、timed_waiting 状态(休眠状态),就需要通过 Thread 类的 interrupt() 方法,让线程从休眠状态进入 runnable 状态,从而结束线程。
这里就涉及到了一个概念“线程中断”,这是一种协作机制,当其他线程通知需要被中断的线程后,线程中断的状态被设置为 true,但是具体被要求中断的线程要怎么处理,完全由被中断线程自己决定,可以在合适的时机中断请求,也可以完全不处理继续执行下去,这样一来,安全性就得到了保障。
Thread类中提供线程中断的方法如下:
- Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为 true(默认是 flase);
- Thread.currentThread().isInterrupted():测试当前线程是否被中断。线程的中断状态会受这个方法的影响,调用一次可以使线程中断状态变为 true,调用两次会使这个线程的中断状态重新转为 false;
- Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态。
Ok,写了那么多,我们来写一个小的demo测试一下线程中断的方法。
【代码示例】
public class Test {
public static void main(String[] args) {
//测试系统监控器
testSystemMonitor();
}
/**
* 测试系统监控器
*/
public static void testSystemMonitor() {
SystemMonitor sm = new SystemMonitor();
sm.start();
try {
//运行 10 秒后停止监控
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("监控任务启动 10 秒后,停止...");
sm.stop();
}
}
/*系统监控器*/
class SystemMonitor {
private Thread t;
//线程中断标识
private volatile boolean stop = false;
/**
* 启动一个线程监控系统
*/
void start() {
t = new Thread(() -> {
while (!stop) {//判断当前线程是否被打断
System.out.println("正在监控系统...");
try {
Thread.sleep(3 * 1000L);//执行 3 秒
System.out.println("任务执行 3 秒");
System.out.println("监控的系统正常!");
} catch (InterruptedException e) {
System.out.println("任务执行被中断...");
//重新设置线程为中断状态,保证JVM抛异常情况下,中断状态仍为true。
Thread.currentThread().interrupt();
}
}
});
t.start();
}
//线程中断
void stop() {
stop = true;
t.interrupt();
}
}
在这里我们先创建了一个SystemMonitor类作为系统检测器,每3秒一循环的进行检测,考虑到在Thread.currentThread().isInterrupted()可能在某些情况下中断失效,所以我们这里自定义一个stop变量,作为线程中断的标识,检测线程启动先对标识位进行判断。
然后,我们在Test类中写一个测试方法,调用这个系统监控器,进行检测,并设置10秒后,调用stop方法中断检测线程,将中断标识stop设置为true。启动代码后,我们在控制台可以看到这样的输出:
正在监控系统...
任务执行 3 秒
监控的系统正常!
正在监控系统...
任务执行 3 秒
监控的系统正常!
正在监控系统...
任务执行 3 秒
监控的系统正常!
正在监控系统...
监控任务启动 10 秒后,停止...
任务执行被中断...
与我们的预期一样,监控线程在执行了3个循环的检测任务后,被成功中断调。到这里,我们就成功的、安全的、优雅的停止了一个线程啦!
结尾彩蛋
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在Java中如何优雅的停止一个线程?可别再用Thread.stop()了!的更多相关文章
- 在Java中如何正确地终止一个线程
1.使用Thread.stop()? 极力不推荐此方式,此函数不安全且已废弃,具体可参考Java API文档 2.设置终止标识,例如: import static java.lang.System.o ...
- Java并发(基础知识)—— 创建、运行以及停止一个线程
在计算机世界,当人们谈到并发时,它的意思是一系列的任务在计算机中同时执行.如果计算机有多个处理器或者多核处理器,那么这个同时性是真实发生的:如果计算机只有一个核心处理器那么就只是表面现象. 现代所有的 ...
- java如何正确停止一个线程
Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的in ...
- java停止一个线程
Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的in ...
- Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程
下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...
- Java 如何正确停止一个线程
自己在做实验性小项目的时候,发现自己遇到一个问题:如何控制线程的"死亡"? 首先,如何开启一个线程呢? 最简单的代码: public class Main { public sta ...
- java中使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
java中使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变? 是引用对象的地址值不能变,引用变量所指向的对象的内容是可以改变. final变量永远指向这个对象,是一个常量指针,而 ...
- JAVA - 请说明”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?
请说明"static"关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法? "static"关键字表明一个成 ...
- Java基础-Java中的并法库之线程池技术
Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.
- Java中对象流使用的一个注意事项
再写jsp的实验作业的时候,需要用到java中对象流,但是碰到了之前没有遇到过的情况,改bug改到崩溃!!记录下来供大家分享 如果要用对象流去读取一个文件,一定要先判断这个文件的内容是否为空,如果为空 ...
随机推荐
- NetCat 工具的常用使用技巧
netcat 黑客们的瑞士军刀,虽然小巧但是其功能一点也不弱,并且该工具天生免杀,值得你去尝试. NCwindows反弹 1:正向连接 服务器执行:nc -l -p 8888 -e cmd.exe 本 ...
- 驱动开发:Win10枚举完整SSDT地址表
在前面的博文<驱动开发:Win10内核枚举SSDT表基址>中已经教大家如何寻找SSDT表基地址了,找到后我们可根据序号获取到指定SSDT函数的原始地址,而如果需要输出所有SSDT表信息,则 ...
- C/C++ 关于继承与多态笔记
继承的基本语法: 继承的目的就是用于提高代码的可用性,减少代码的重复内容,高效开发. #include <iostream> using namespace std; class Base ...
- C/C++ Qt QThread 线程组件应用
QThread库是QT中提供的跨平台多线程实现方案,使用时需要继承QThread这个基类,并重写实现内部的Run方法,由于该库是基本库,默认依赖于QtCore.dll这个基础模块,在使用时无需引入其他 ...
- Gin 响应方式
响应 1. 字符串方式 r.GET("/user/save", func(ctx *gin.Context) { ctx.String(http.StatusOK, "t ...
- GoodSync(最好的文件同步软件)
GoodSync是一款使用创新的最好的文件同步软件,可以在你的台式机.笔记本.USB外置驱动器等设备直接进行数据文件同步软件. GoodSync将高度稳定的可靠性和极其简单的易用性完美结合起来,可以实 ...
- Windows 7 Ultimate with Service Pack 1 (x64)
Windows 7 Ultimate with Service Pack 1 (x64) 链接:https://pan.baidu.com/s/1ZHODDFlJPLQ3ydxZ4rfc3g 提取码: ...
- Dash 2.15版本新特性介绍
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/dash-master 大家好我是费老师,Dash不久前发布了其2.15.0版本,新增了一些实用的特性 ...
- 2024-02-03:用go语言,你有 k 个背包。给你一个下标从 0 开始的整数数组 weights, 其中 weights[i] 是第 i 个珠子的重量。同时给你整数 k, 请你按照如下规则将所有
2024-02-03:用go语言,你有 k 个背包.给你一个下标从 0 开始的整数数组 weights, 其中 weights[i] 是第 i 个珠子的重量.同时给你整数 k, 请你按照如下规则将所有 ...
- korean doll likeness模型|Japanese-doll-likeness模型获取及使用
1.模型 之前给大家写了Mac安装stable-diffusion-webui绘制AI妹子保姆级教程,教程在下面 [奶奶看了也不会]AI绘画 Mac安装stable-diffusion-webui绘制 ...