Java并发编程之如何正确的停止线程

在Java线程状态转换中,我们知道,线程最终的命运是Terminated,当然,也有永不停止一直干活的线程(除非断电)。线程的停止,正常来说是线程运行到结束,但也有程序出错或是用户关闭程序等原因造成的线程终止。

如何正确停止线程?

正确的停止线程对保护程序数据有重要意义,如果线程运行于一半,执行了一些数据操作,需要后续代码继续运行才能保证数据正确,此时假若你运行了下面的强制关闭线程代码

thread.stoo();

那大概率你的数据将变脏。这个接口虽说可以用,但需要开发人员明确知道使用此接口调用不会造成程序数据异常才能用,JDK已经将此方法设置为过时,就是为了避免开发人员误用。

正确的停止线程姿态应该是通知、协作。更明确的说,就是使用interrupt方法去替代stop方法。下面是一个例子

/*** @author kangming.ning* @date 2020/10/12 19:08*/
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(20);thread.interrupt();}
}

可以看出,上面我使用了interrupt方法去通知线程停止,而线程内部,则在执行代码处先判断是否接收到中断信号,一但接收到中断信号,直接让线程退出执行,线程自然结束。这里需要注意的是下面这句代码

Thread.currentThread().isInterrupted()

这个在线程启动时值为false,当线程调用了interrupt方法,线程实际就是将此值设置为true。interrupt方法并不会去将你的线程给强制终止,而仅仅是通知你。该怎么干,线程自己决定。这个就是通知与协作。

通知与协作的好处

通过上面的例子,可以很容易看出,通知与协作将停止的权利交还给了线程自己,线程接收到停止信号后,可以先完成工作再结束线程。也可以对信号视而不见,这完全由开发人员决定。这样的线程它不会出现不可预料的错误。

InterruptedException 异常处理

上面给出了一个正确停止线程的例子,但实际应用线程可能会复杂,比如在线程内部调用sleep、wait等可以阻塞线程的方法,此时如果调用interrupt方法,中断标志首先会设置为true。当阻塞方法会抛出InterruptedException异常,中断标识设置为false。此时如果程序设计不当,可能会导致线程停不下来,比如下面的例子

public class NeedReInterrupt implements Runnable {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new NeedReInterrupt());thread.start();Thread.sleep(200);thread.interrupt();}@Overridepublic void run() {int count = 0;while (!Thread.currentThread().isInterrupted() && count < 10000) {try {System.out.println("count = " + count++);Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}}}
}

可以看到,在while循环里面,程序捕获了InterruptedException,打印了异常,未有更多操作,此时这个中断异常就被程序吃掉了,这线程将会继续执行循环体直到结束,主线程调用的interrupt并未得到任务效果。当然,如果将while循环放到try里面,程序可正确响应主线程的停止,因为程序不再需要执行判断语句。

像上面的程序出错的原因在于,判断中断信号的位置不当导致。如果确实需要这么设置,可以在catch处再次调用中断,这样一来,循环体就能再次发现中断信号为true,正确结束程序。

    @Overridepublic void run() {int count = 0;while (!Thread.currentThread().isInterrupted() && count < 10000) {try {System.out.println("count = " + count++);Thread.sleep(20);} catch (InterruptedException e) {
//重新设置中断标示 抛出InterruptedException后中断标示会被清除 如果不重新设置的话,整个线程将无法中断Thread.currentThread().interrupt();e.printStackTrace();}}}

如果是在某方法中有相关阻塞方法,可以将方法签名设置抛出InterruptedException,直抛到线程的run方法,run方法catch中断异常进行处理。

死锁线程无法中断

不同于sleep和wait等阻塞情况,死锁导致的阻塞,中断信号是无法被感知的,程序设计尤其要注意这点,下面是一个死锁的例子

public class DeadLockThread extends Thread {public static void main(String args[]) throws Exception {final Object lock1 = new Object();final Object lock2 = new Object();Thread thread1 = new Thread() {public void run() {deathLock(lock1, lock2);}};Thread thread2 = new Thread() {public void run() {// 注意,这里在交换了一下位置deathLock(lock2, lock1);}};System.out.println("Starting thread...");thread1.start();thread2.start();Thread.sleep(3000);System.out.println("Interrupting thread...");thread1.interrupt();thread2.interrupt();Thread.sleep(3000);System.out.println("Stopping application...");}static void deathLock(Object lock1, Object lock2) {try {synchronized (lock1) {Thread.sleep(10);// 不会在这里死掉synchronized (lock2) {// 会锁在这里,虽然阻塞了,但不会抛异常System.out.println(Thread.currentThread());}}} catch (InterruptedException e) {e.printStackTrace();System.exit(1);}}
}

上面的例子中,两条线程地都永远的阻塞,调用interrupt不会产生任何的作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/46779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

new mars3d.graphic.FixedRoute({的position长度超过一百条浏览器会卡死的解决方案

问题场景描述&#xff1a; FixedRoute的position数据已经很精细时&#xff0c;会导致卡死的问题 解决方案&#xff1a; 1. 数据已经很精细时&#xff0c;可以不用autoSurfaceHeight来计算&#xff0c;如果非要用&#xff0c;可以加个minDistance: 200参数。 fixedRoute.auto…

ChatGPT 深度解析:技术驱动的智能对话

在当今科技飞速发展的时代&#xff0c;ChatGPT 无疑成为了最耀眼的明星之一。它以其令人惊叹的智能对话能力&#xff0c;引发了全球范围内的广泛关注和热议。 ChatGPT 背后的技术堪称精妙绝伦。它基于深度学习算法&#xff0c;通过对海量数据的学习和分析&#xff0c;从而能够理…

BasicSR项目(通用图像超分、修复、增强工具库)介绍

项目地址&#xff1a;https://github.com/XPixelGroup/BasicSR 文档地址&#xff1a;https://github.com/XPixelGroup/BasicSR-docs/releases BasicSR 是一个开源项目&#xff0c;旨在提供一个方便易用的图像、视频的超分、复原、增强的工具箱。BasicSR 代码库从2018年4月20日…

【Memcached】Memcached的工作原理

目录 ​编辑 第2章&#xff1a;Memcached工作原理 2.1 数据存储与访问 2.2 分布式架构 2.3 数据过期机制 第2章&#xff1a;Memcached工作原理 2.1 数据存储与访问 Memcached是一种键值存储系统&#xff0c;其中数据以键值对的形式存储。键是用于定位数据的唯一标识符&am…

MySQL架构详解

MySQL是一个广泛使用的开源关系数据库管理系统&#xff0c;以其可靠性、性能和易用性而闻名。了解MySQL的架构对于优化数据库性能、设计高效的数据库系统以及进行有效的数据库管理至关重要。本文将详细介绍MySQL的架构&#xff0c;包括其主要组件和功能。 1. 连接器&#xff0…

libyaml库的交叉编译

目录 1.Ubuntu环境中安装libyaml库 2.交叉编译 3.success 1.Ubuntu环境中安装libyaml库 官方地址&#xff1a;https://pyyaml.org/wiki/LibYAML 下载路径&#xff1a;http://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz 2.交叉编译 官方的下载路径为/usr/local下&am…

【unity实战】使用unity制作一个红点系统

前言 注意&#xff0c;本文是本人的学习笔记记录&#xff0c;这里先记录基本的代码&#xff0c;后面用到了再回来进行实现和整理 素材 https://assetstore.unity.com/packages/2d/gui/icons/2d-simple-ui-pack-218050 框架&#xff1a; RedPointSystem.cs using System.…

PHP全功能微信投票迷你平台系统小程序源码

&#x1f525;让决策变得超简单&#xff01;&#x1f389; &#x1f680;【一键创建&#xff0c;秒速启动】 嘿小伙伴们&#xff0c;你还在为组织投票而手忙脚乱吗&#xff1f;来试试这款全功能投票迷你微信小程序吧&#xff01;只需轻轻一点&#xff0c;无论是班级选举、社团…

Java创建对象除了new还有别的什么方式?

通过反射创建对象&#xff1a;通过 Java 的反射机制可以在运行时动态地创建对象。可以使用 Class 类的 newInstance() 方法或者通过 Constructor 类来创建对象 public class MyClass { public MyClass() { // Constructor } } public class Main { public…

初学者指南:如何搭建和配置 Nginx 服务器

初学者指南&#xff1a;如何搭建和配置 Nginx 服务器 Nginx 是一个高性能的 HTTP 和反向代理服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。本文将详细介绍如何在 Linux 上安装、配置和管理 Nginx 服务器。 一、安装 Nginx Nginx 可以安装在多种操作系统上&#x…

c# 依赖注入-服务的生命周期

在 C# 中&#xff0c;依赖注入服务的生命周期指的是在应用程序中管理和控制依赖项注入服务对象的生命周期的方式。常见的生命周期包括瞬态&#xff08;transient&#xff09;、作用域&#xff08;scoped&#xff09;和单例&#xff08;singleton&#xff09;三种。 瞬态&#…

【postgresql】pg_dump备份数据库

pg_dump 介绍 pg_dump 是一个用于备份 PostgreSQL 数据库的实用工具。它可以将数据库的内容导出为一个 SQL 脚本文件或其他格式的文件&#xff0c;以便在需要时进行恢复或迁移。 基本用法 pg_dump [选项] [数据库名] 命令选项 -h 或 --host&#xff1a;指定数据库服务器的主…

2024年大数据高频面试题(上篇)

文章目录 HDFS读流程和写流程HDFS读数据流程NameNode和Secondary NameNode工作机制FsimageEdits文件Seen_txidnamenode工作机制HA NameNode如何工作ZKFCHealthMonitorActiveStandbyElectorJouranlNode集群DataNode工作机制DataNode数据损坏压缩MapReduce工作流程MapTask工作流R…

Visual Studio远程调试工具

路径&#xff1a;Visual Studio安装路径/Common7/IDE/Remote Debugger/平台/msvsmon.exe。 平台有x86、x64&#xff0c;x64即可调试x86进程也可调试x64进程。 将平台路径下的所有文件拷贝至其他PC&#xff0c;运行msvsmon.exe。 工具栏选择“工具&#xff08;T&#xff09;”…

Ubuntu18.04安装ROS

1.添加ROS软件源 sudo sh -c echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.listcurl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc输入指令&#xff1a;curl -s https:…

密码学原理精解【6】

文章目录 置换密码密码体制加解密过程置换置换运算定义置换运算的例子集合与置换置换规则两行表示法轮换表示法置换运算的结果置换的性质注意事项 分组加解密 理论基础1. 准备工作2. 置换过程3. 置换密码的具体实现方式4. 安全性分析5. 置换密码的应用代换密码代换密码的工作原…

Python中EMD的安装教程

第一步&#xff1a;首先安装两个包 pip install pyemd pip install EMD-signal第二步&#xff1a;然后&#xff0c;进行改名 安装完之后&#xff0c;找到包所在的位置&#xff0c;然后要将原来pyemd的文件夹名称改为PyEMD&#xff1a;

cleanshot Mac 上的截图工具

笔者闲来无事&#xff0c;最近在找一些mac上好用的工具其中一款就是cleanShot。为什么不用原有的mac自带的呢。因为相对来说编辑功能不算全面&#xff0c;不支持长截图。那有没有一款软件支持关于截图的好用工具呢。 所以笔者找了这款。安装包是直接安装就可使用的。请大家点赞…

Golang | Leetcode Golang题解之第237题删除链表中的节点

题目&#xff1a; 题解&#xff1a; func deleteNode(node *ListNode) {node.Val node.Next.Valnode.Next node.Next.Next }

Python中的UnboundLocalError是什么错误?如何解决?

如果代码报错UnboundLocalError, 大概率犯了以下错误&#xff1a; money 10000 # 当前存款def add_money(value):money valueif __name__ __main__:print(当前存款:, money)add_money(1000)print(当前存款:, money)其中&#xff0c;变量money表示当前存款&#xff1b;函数…