如何终止一个线程
如何终止一个线程
是使用 thread.stop() 吗?
public class ThreadDemo extends Thread{@Overridepublic void run() {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("this is demo thread :"+Thread.currentThread().getName());}public static void main(String[] args) throws InterruptedException {ThreadDemo t = new ThreadDemo();t.start();System.out.println("this is main thread :"+Thread.currentThread().getName());t.stop();}
}
实际代码中的stop方法是不建议使用的:
t.stop ();
Java 的rt.jar 包中的Thread类的 stop() 源码:
/*** Forces the thread to stop executing.* <p>* If there is a security manager installed, its <code>checkAccess</code>* method is called with <code>this</code>* as its argument. This may result in a* <code>SecurityException</code> being raised (in the current thread).* <p>* If this thread is different from the current thread (that is, the current* thread is trying to stop a thread other than itself), the* security manager's <code>checkPermission</code> method (with a* <code>RuntimePermission("stopThread")</code> argument) is called in* addition.* Again, this may result in throwing a* <code>SecurityException</code> (in the current thread).* <p>* The thread represented by this thread is forced to stop whatever* it is doing abnormally and to throw a newly created* <code>ThreadDeath</code> object as an exception.* <p>* It is permitted to stop a thread that has not yet been started.* If the thread is eventually started, it immediately terminates.* <p>* An application should not normally try to catch* <code>ThreadDeath</code> unless it must do some extraordinary* cleanup operation (note that the throwing of* <code>ThreadDeath</code> causes <code>finally</code> clauses of* <code>try</code> statements to be executed before the thread* officially dies). If a <code>catch</code> clause catches a* <code>ThreadDeath</code> object, it is important to rethrow the* object so that the thread actually dies.* <p>* The top-level error handler that reacts to otherwise uncaught* exceptions does not print out a message or otherwise notify the* application if the uncaught exception is an instance of* <code>ThreadDeath</code>.** @exception SecurityException if the current thread cannot* modify this thread.* @see #interrupt()* @see #checkAccess()* @see #run()* @see #start()* @see ThreadDeath* @see ThreadGroup#uncaughtException(Thread,Throwable)* @see SecurityManager#checkAccess(Thread)* @see SecurityManager#checkPermission* @deprecated This method is inherently unsafe. Stopping a thread with* Thread.stop causes it to unlock all of the monitors that it* has locked (as a natural consequence of the unchecked* <code>ThreadDeath</code> exception propagating up the stack). If* any of the objects previously protected by these monitors were in* an inconsistent state, the damaged objects become visible to* other threads, potentially resulting in arbitrary behavior. Many* uses of <code>stop</code> should be replaced by code that simply* modifies some variable to indicate that the target thread should* stop running. The target thread should check this variable* regularly, and return from its run method in an orderly fashion* if the variable indicates that it is to stop running. If the* target thread waits for long periods (on a condition variable,* for example), the <code>interrupt</code> method should be used to* interrupt the wait.* For more information, see* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.*/@Deprecatedpublic 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 statesstop0(new ThreadDeath());}
源码中标注了已弃用,并且给出来详细的解释和说明:
已弃用
这种方法本质上是不安全的。使用thread.stop停止线程会导致它解锁它锁定的所有监视器(这是未选中的ThreadDeath异常向堆栈上传播的自然结果)。
如果以前受这些监视器保护的任何对象处于不一致状态,则其他线程会看到损坏的对象,从而可能导致任意行为。stop的许多用法应该被简单地修改一些变量以指示目标线程应该停止运行的代码所取代。
目标线程应该定期检查该变量,如果该变量指示要停止运行,则以有序的方式从其运行方法返回。
如果目标线程长时间等待(例如,在条件变量上),则应使用中断方法中断等待。
操作系统在执行stop时,并不知道Java当前的线程情况。
假设run方法中有多条指令在执行。
如果通过stop强制终止,那么可能会导致数据不完整,所以不建议这样使用。
那如何解决呢?
线程中断
可以通过interrupt 来做。但是interrupt不是用来终止的方法,而是发送一个信号,需要对其进行额外处理才可以起到终止的效果。
public class InterruptDemo implements Runnable{private int i=0;@Overridepublic void run() {//isInterrupted 默认falsewhile(!Thread.currentThread().isInterrupted()){System.out.println("this is Thread : "+ (i++) +" - " +Thread.currentThread().getName());}}public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new InterruptDemo());t.start();Thread.sleep(1000);//主线程timed_waiting 1秒t.interrupt();//设置 interrupted=true}
}
执行结果:
......
this is Thread : 69 - Thread-0
this is Thread : 70 - Thread-0
this is Thread : 71 - Thread-0Process finished with exit code 0
当我们的run
方法中,如果存在一个无法终止的操作时(比如while(true)
、比如sleep(200000)
)
它们会使线程阻塞,无法运行,也无法终止,这时要考虑使用interrupt 来中断。
线程复位
在Java线程中,针对阻塞的操作,如果想去唤醒它,它会提供一个中断的异常抛出。
但凡是让线程阻塞的机制(方法),都会抛出中断异常(InterruptedException
),需要去处理。
import java.util.concurrent.TimeUnit;public class InterruptExceptionDemo implements Runnable{@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()){try {TimeUnit.SECONDS.sleep(200);} catch (InterruptedException e) {//触发了线程复位 isInterrupted-> falsee.printStackTrace();}}System.out.println("thread is end.");}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new InterruptExceptionDemo());thread.start();Thread.sleep(1000);thread.interrupt();}
}
控台打印结果:
我们中thread.interrupt() 后,run方法响应来异常,进入catch代码块。
由于触发了线程复位,所以isInterrupted 又重置为 false。
我们需要中catch块中对中断做出处理,可以有的处理方式:
- 如果不做处理,那么由于中断复位会使线程继续处于sleep状态。
- 继续中断,将选择权给到run方法,Thread.currentThread().interrupt(); //再次中断
- 抛出异常。
复位是JVM层面做的,它会在抛异常之前,设置状态为false,即复位。
为什么复位,因为我们处理当前线程的权限应该由线程本身来控制,如果不复位的话,下次循环就会终止,就相当于线程的终止由线程外部来控制了。
线程终止的本质
public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupt0(); // Just to set the interrupt flagb.interrupt(this);return;}}interrupt0();}private native void interrupt0();
我们可以看到,Thread的interrupt
方法,最终是调动了一个native的interrupt0
方法。
而在JVM中,我们可以看到 interrupt0 做了什么。
我们先来看JVM的 jvm.cpp:
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))JVMWrapper("JVM_Interrupt");// Ensure that the C++ Thread and OSThread structures aren't freed before we operateoop java_thread = JNIHandles::resolve_non_null(jthread);MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);// We need to re-resolve the java_thread, since a GC might have happened during the// acquire of the lockJavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));if (thr != NULL) {Thread::interrupt(thr);}
JVM_END
其中最后的 Thread::interrupt(thr) 调用了操作系统层面的中断。
我们找到linux对应的os_linux.cpp
文件:
进入文件找到interrupt方法如下:
// interrupt supportvoid os::interrupt(Thread* thread) {assert(Thread::current() == thread || Threads_lock->owned_by_self(),"possibility of dangling Thread pointer");OSThread* osthread = thread->osthread();if (!osthread->interrupted()) {osthread->set_interrupted(true);//设置一个中断状态// More than one thread can get here with the same value of osthread,// resulting in multiple notifications. We do, however, want the store// to interrupted() to be visible to other threads before we execute unpark().OrderAccess::fence();ParkEvent * const slp = thread->_SleepEvent ;//如果是sleep中,唤醒if (slp != NULL) slp->unpark() ;}// For JSR166. Unpark even if interrupt status already was setif (thread->is_Java_thread())((JavaThread*)thread)->parker()->unpark();ParkEvent * ev = thread->_ParkEvent ;if (ev != NULL) ev->unpark() ;}
上面代码是os的interrupt逻辑,会发现,它不仅仅是设置来一个interrupted变量值,
而且还有 upark的唤醒逻辑。
interrupt()
- 设置一个共享变量的值 true
- 唤醒处于阻塞状态下的线程
相关文章:

如何终止一个线程
如何终止一个线程 是使用 thread.stop() 吗? public class ThreadDemo extends Thread{Overridepublic void run() {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("this is demo thread :"Thre…...

上岸!选择你的隐私计算导师!
开放隐私计算 开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神,专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播,愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号…...

go gin学习记录5
有了前面几节的学习,如果做个简单的web服务端已经可以完成了。 这节来做一下优化。 我们实验了3种SQL写入的方法,但是发现每一种都需要在方法中去做数据库链接的操作,有些重复了。 所以,我们把这部分提取出来,数据库链…...

PyQt5数据库开发2 5.1 QSqlQueryModel
目录 一、Qt窗体设计 1. 新建Qt项目 2. 拷贝4-3的部分组件过来 3. 添加资源文件 4. 创建Action 5. 添加工具栏 6. 创建菜单项 7. 关闭Action的实现 8. 调整布局 8.1 调整两个groupbox的布局 8.3 为窗体设置全局布局 二、代码拷贝和删除 1. 新建项目目录 2. 编译…...

MySQL-redo log和undo log
什么是事务 事务是由数据库中一系列的访问和更新组成的逻辑执行单元 事务的逻辑单元中可以是一条SQL语句,也可以是一段SQL逻辑,这段逻辑要么全部执行成功,要么全部执行失败 举个最常见的例子,你早上出去买早餐,支付…...

阿里云ECS TOP性能提升超20%!KeenTune助力倚天+Alinux3达成开机即用的全栈性能调优 | 龙蜥技术
文/KeenTune SIG01阿里云 ECS 上售卖页新增“应用加速”功能2023年1月12日 阿里云 ECS 的售卖页有了一些新的变化,在用户选择倚天 Alinux3 新建实例时,多了一个新的选项“应用加速”。这个功能是 阿里云 ECS 基于 KeenTune 提供典型云场景的开机即用的全…...

华为OD机试真题Python实现【快递业务站】真题+解题思路+代码(20222023)
快递业务站 题目 快递业务范围有 N 个站点,A 站点与 B 站点可以中转快递,则认为 A-B 站可达, 如果 A-B 可达,B-C 可达,则 A-C 可达。 现在给 N 个站点编号 0、1、…n-1,用 s[i][j]表示 i-j 是否可达, s[i][j] = 1表示 i-j可达,s[i][j] = 0表示 i-j 不可达。 现用二维…...

【c语言】预处理
🚀write in front🚀 📜所属专栏:> c语言学习 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是…...

嵌入式常用知识
12、并发和并行的区别? 最本质的区别就是:并发是轮流处理多个任务,并行是同时处理多个任务。 你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。 你吃饭吃到一半&…...

和平精英五曜赐福返场,老款玛莎返场来了
和平精英五曜赐福返场,老款玛莎返场来了!新款如何选择! 关于返场的新消息,都说云南百收SEO解说消息不准,之前看过文章的应该会知道,全网只有云南百收SEO解说发了。玛莎返场,快喊你的阿姨来看&a…...

React从入门到精通二
React从入门到精通之购物车案例1. 购物车需求说明使用到的data list2. 项目code1. 购物车需求说明 list data展示到列表中每个item的通过按钮来控制购买的数据量删除按钮可以删除当前的itemTotal Price计算当前购物车的总的价格 使用到的data list const books [{id: 1,name…...

【likeshop多商户】电子面单商家直播上线啦~
likeshop多商户商城v2.2.0版本更新啦! 新增功能: 商家直播 单子面单 优化: 个人中心优惠券数量统计优化 修复: 秒杀商品待审核时,下单价格计算错误 个人中心修改头像后地址保存错误 「商家直播」 提升品牌知名度…...

游戏化销售管理是什么?使用CRM系统进行有什么用?
对于企业销售来说,高薪酬也伴随着更高的压力与挑战。高强度的单一工作会让销售人员逐渐失去对工作的兴趣,导致销售状态缺少动力和激情,工作开展愈加困难。您可以通过CRM系统进行游戏化销售管理,让销售人员重新干劲满满。 游戏并不…...

Mysql 索引(三)—— 不同索引的创建方式(主键索引、普通索引、唯一键索引)
了解了主键索引的底层原理,主键索引其实就是根据主键字段建立相关的数据结构(B树),此后在使用主键字段作为条件查询时,会直接根据主键查找B树的叶子结点。除了主键索引外,普通索引和唯一键索引也是如此&…...

秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
本文将带领大家亲手实现一个垃圾信息过滤的算法。 在正式讲解算法之前,最重要的是对整个任务有一个全面的认识,包括算法的输入和输出、可能会用到的技术,以及技术大致的流程。 本任务的目标是去识别一条短信是否为垃圾信息,即输入为一条文本信息,输出为二分类的分类结果。…...

SpringCloud - Feign远程调用
目录 Feign的远程调用 RestTemplate方式调用存在的问题 介绍与初步使用 Feign的自定义配置 Feign运行自定义配置来覆盖默认配置,可以修改的配置如下: 配置Feign日志有两种方式: Feign性能优化 Feign底层的客户端实现: 连…...

Eotalk Vol.03:结合 API DaaS,让使用数据更方便
Eotalk 是由 Eolink CEO 刘昊臻发起的泛技术聊天活动,每期都会邀请一些技术圈内的大牛聊聊天,聊些关于技术、创业工作、投融资等热点话题。 Eotalk 的第 3 期,很高兴邀请到 Tapdata CEO TJ 唐建法,TJ 可以说是一位超级大咖&#x…...

从零开始学习Java编程:一份详细指南
Java入门Java简介和历史Java开发环境的安装和配置Java开发工具的介绍和使用(例如Eclipse、IntelliJ IDEA等)Java语言的基本概念(例如变量、数据类型、运算符、流程控制语句等)面向对象编程基础面向对象编程概念和基本原则类和对象…...

电子技术——系统性分析反馈电压放大器
电子技术——系统性分析反馈电压放大器 在本节我们提供一个系统性的分析反馈电压放大器的方法。首先我们考虑反馈网络没有负载效应理想情况,其次我们考虑反馈网络有限阻抗下的非理想情况。总之,这种方法的思路在于,将非理想情况转换为理想情况…...

【C语言进阶】结构体、位段、枚举、以及联合(共用体)的相关原理与使用
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.结构体1.1 概述&a…...

《蓝桥杯每日一题》哈希·AcWing 2058. 笨拙的手指
1.题目描述每当贝茜将数字转换为一个新的进制并写下结果时,她总是将其中的某一位数字写错。例如,如果她将数字 14 转换为二进制数,那么正确的结果应为 1110,但她可能会写下 0110 或 1111。贝茜不会额外添加或删除数字,…...

Linux 定时任务调度(crontab)
一、Crontab Crontab命令用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。 可以使用Crontab定时处理离线任务,比如每天凌晨2点更新数据等,经常用于系统任务调度。…...

C进阶:6.C语言文件操作
目录 1.为什么使用文件 2.什么是文件 2.1程序文件 2.2数据文件 2.3文件名 3.文件的打开和关闭 3.1文件指针 4.文件的顺序读写 fputc()写入文件 fgetc()从文件中读取 fgets()读取一段字符串 fprintf格式化写入文件、fscanf格式化读出文件 4.1对比一组函数 5.文件…...

Linux环境变量
Linux环境变量孤儿进程进程优先级其他概念环境变量感性的理解环境变量常见的环境变量添加环境变量环境变量的组织形式通过代码如何获取环境变量再次理解环境变量命令行参数孤儿进程 概念:父进程先于子进程结束,这样的子进程就叫做“孤儿进程”; “孤儿”…...

Kotlin-委托、代理和单例对象
委托和代理 实现委托和代理,使用的是by关键字。 这里设计一个场景:假设某个演员被要求唱歌,但是不会唱歌,就委托一个会唱歌的歌手在后台唱歌。 如何实现这个需求,下面就开始直接写代码 首先定义一个唱歌能力接口 int…...

华为OD机试真题Python实现【报数】真题+解题思路+代码(20222023)
报数 题目 一百个人围成一圈,每个人有一个编码编号从一开始到一百。 他们从一开始依次报数,报道M的人自动退出圈圈, 然后下一个人接着从1开始报数一直到剩余人数小于M。 请问最后剩余人在原先的编码为多少? 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Py…...

MacOS:Error message “error:0308010C:digital envelope routines::unsupported“
命令行:export NODE_OPTIONS--openssl-legacy-provider 原帖:https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported...

Java 异常处理,超详细整理,适合新手入门
目录 前言 抛出异常 捕获异常 处理异常 finally块 总结 前言 当Java程序中出现错误或异常时,通常会抛出一个异常。Java的异常处理机制使得我们可以在程序运行过程中捕获这些异常并采取相应的措施,以便程序能够正常运行或者优雅地停止。 抛出异常 在…...

23年了,GOPATH和go.mod 还在冲突!
现在 新配了 go环境 设置了GOROOT,GOPATH ,发现引用别的包会出问题。一直会报 package XX not in GOROOT (xxxx)我的目录:我的开发目录: /home/fate/go_projects/老样子,下面有 /home/fate/go_…...

Could not connect to Redis at 127.0.0.1:6379: 由于目标计算机积极拒绝,无法连接。(极简解决办法)
一、遇到问题。 在需要启动Redis客户端的时候,会发现会报这个错误。报这个错误的原因就是Redis的服务端没有开启,那Redis的客户端是访问不了的 二、解决办法。 1.解决的办法就是要启动服务端,让这个客户端可以访问到。启动服务端最简单不会…...