震惊!这样终止线程,竟然会导致服务宕机?

在开始之前,我们先来看以下代码会有什么问题?

public class ThreadStopExample {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {System.out.println("子线程开始执行");// 模拟业务处理Thread.sleep(1000);} catch (Exception e) { }// 伪代码:重要的业务方法System.out.println("子线程的重要业务方法");});t1.start();// 让子线程先运行一点业务Thread.sleep(100);// 终止子线程t1.stop();// 等待一段时间,确保子线程“执行完”Thread.sleep(3000);System.out.println("主线程执行完成");}
}

或许你已经发现了,上面这段代码使用了 Thread.stop() 来终止线程,在 Java 程序中是不允许这样终止线程的。什么?你问为什么不能这样?

首先来说 IDE 都会鄙视你了,它会阻止你使用 Thread.stop()

什么?你不信。那么来看这张图:
image.png

好吧,那为什么不能这样用呢?总得给我一个敷衍的理由吧?

问题一:破坏了程序的完整性

其实是这样的,以文章刚开头的那段代码来说,它的执行结果是:

子线程开始执行

主线程执行完成

我们发现了一个惊天的大问题,最重要的那段伪代码竟然没执行,如下图所示:
image.png

可以看出使用 stop() 终止线程之后,线程剩余的部分代码会放弃执行,这样会造成严重的且不易被发现的惊天大 Bug,假如没有执行的那段代码是释放系统资源的代码,或者是此程序的主要逻辑处理代码。这就破坏了程序基本逻辑的完整性,导致意想不到的问题发生,而且它还很隐秘,不易被发现和修复。

有人说,这还不简单,我加个 finally 不就完了吗?

这???杠精哪都有,今年特别多。

行,既然这个说服不了你,咱接着往下看。

问题二:破坏了原子逻辑

我们知道在 Java 中 synchronized 属于独占式可重入悲观锁,如果我们使用它修饰代码,妥妥的多线程没问题,但如果碰到 stop() 方法就不一定了,直接来看代码吧。

public class ThreadStopExample {public static void main(String[] args) throws InterruptedException {MyThread myThread = new MyThread();Thread t2 = new Thread(myThread);// 开启线程t2.start();for (int i = 0; i < 10; i++) {Thread t = new Thread(myThread);t.start();}// 结束线程t2.stop();}/*** 自定义原子测试线程*/static class MyThread implements Runnable {// 计数器int num = 0;@Overridepublic void run() {// 同步代码块,保证原子操作synchronized (MyThread.class) {// 自增num++;try {// 线程休眠 0.1 秒Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}// 自减num--;System.out.println(Thread.currentThread().getName() + " | num=" + num);}}}
}

以上程序的执行结果为:

Thread-5 | num=1

Thread-4 | num=1

Thread-2 | num=1

Thread-1 | num=1

Thread-8 | num=1

Thread-6 | num=1

Thread-9 | num=1

Thread-3 | num=1

Thread-7 | num=1

Thread-10 | num=1

从结果可以看出,以上代码经过 synchronized 修饰的 ++ 和 – 操作,到最后打印的结果 num 竟然不是 0,而是 1。

这是因为 stop() 方法会释放此线程中的所有锁,导致程序执行紊乱,破坏了程序的原子操作逻辑

以上的这些问题,导致了 JDK 废弃了 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>.*/
@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 statesstop0(new ThreadDeath());
}

可以看出 stop() 方法被 @Deprecated 注释修饰了,而被此注解修饰的代码表示为过时方法,不建议被使用。从 stop() 的备注信息可以看出,官方也不建议使用 stop() ,说它是一个非安全的方法。

正确终止线程

那如何终止线程呢?这里提供 2 个正确的方法:

  1. 设置退出标识退出线程;
  2. 使用 interrupt() 方法终止线程。

1.自定义退出标识

我们可以自定义一个布尔变量来标识是否需要退出线程,实现代码如下:

// 自定义退出标识退出线程
static class FlagThread extends Thread {public volatile boolean exit = false;public void run() {while (!exit) {// 执行正常的业务逻辑}}
}

可以看出我们使用了关键字 volatile 对线程进行了修饰,这样就可以保证多线程的执行安全了,在我们需要让线程退出时,只需要把变量 exit 赋值为 true 就可以了。

2.interrupt 终止线程

当我们使用 interrupt() 方法时,以上两个示例的执行结果就正常了,执行代码如下:

public class ThreadStopExample {public static void main(String[] args) throws InterruptedException {// 问题一:破坏了程序的完整性Thread t1 = new Thread(() -> {try {System.out.println("子线程开始执行");// 模拟业务处理Thread.sleep(1000);} catch (Exception e) { }// 伪代码:重要业务方法System.out.println("子线程的重要业务方法");});t1.start();// 让子线程先运行一点业务Thread.sleep(100);// 终止子线程t1.interrupt();// 等待一段时间,确保子线程“执行完”Thread.sleep(3000);System.out.println("主线程执行完成");// 问题二:破坏了原子逻辑MyThread myThread = new MyThread();Thread t2 = new Thread(myThread);// 开启线程t2.start();for (int i = 0; i < 10; i++) {Thread t = new Thread(myThread);t.start();}// 结束线程t2.interrupt();}/*** 自定义原子测试线程*/static class MyThread implements Runnable {// 计数器int num = 0;@Overridepublic void run() {// 同步代码块,保证原子操作synchronized (MyThread.class) {// 自增num++;try {// 线程休眠 0.1 秒Thread.sleep(100);} catch (InterruptedException e) {System.out.println(e.getMessage());}// 自减num--;System.out.println(Thread.currentThread().getName() + " | num=" + num);}}}
}

以上程序的执行结果为:

子线程开始执行

子线程的重要业务方法

主线程执行完成

sleep interrupted

Thread-1 | num=0

Thread-9 | num=0

Thread-10 | num=0

Thread-7 | num=0

Thread-6 | num=0

Thread-5 | num=0

Thread-4 | num=0

Thread-2 | num=0

Thread-3 | num=0

Thread-11 | num=0

Thread-8 | num=0

可以看出以上的执行都符合我们的预期,这才是正确的终止线程的方式。

总结

本文我们讲了线程的三种终止方式,自定义退出标识的方式、使用 stop() 的方式或 interrupt() 的方式。其中 stop() 的方式会导致程序的完整性和原子性被破坏的问题,并且此方法被 JDK 标识为过期方法,不建议使用,而 interrupt() 方法无疑是最适合我们的终止线程的方式。

更多精彩内容,请关注微信公众号「Java中文社群」

Java中文社群公众号二维码

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

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

相关文章

华为交换机系统软件升级和安全漏洞修复教程

华为交换机官网 可以查询到华为交换机官方电话:400-822-9999,通过下面2个命令查询出需要升级的交换机软件版本和补丁版本号发给华为,获取新的升级系统软件和补丁以及升级教程。下面是我升级华为交换机总结的教程,作为参考 <HUAWEI> display version<HUAWEI&g…

python里x.pow2_带有Python示例的math.pow()方法

python里x.pow2Python math.pow()方法 (Python math.pow() method) math.pow() method is a library method of math module, it is used to calculate the given power of a base, it accepts two numbers and returns the first number to the power of the second number in…

想读Spring源码?先从这篇「 极简教程」开始

这是我的第 47 篇原创文章。为什么要阅读源码&#xff1f;这是一个有趣的问题&#xff0c;类似的问题还有&#xff0c;为什么要看书&#xff1f;为什么要爬山&#xff1f;这也是一个哲学问题&#xff0c;我想每个人都有不同的答案&#xff0c;下面我是对阅读源码好处的一些思考…

Linux Shell接收键盘输入

1.read命令格式 read [选项] [变量名] 选项&#xff1a; -p “提示信息”&#xff1a;在等待read输入时&#xff0c;输出提示信息 -t “秒数”&#xff1a; read命令会一致等待用户输入&#xff0c;使用此选项可以指定等待时间 -n “字符数”&#xff1a; read命令只接受指…

机房收费重构之总结篇

《机房收费重构》之路终于接近了尾声&#xff0c;这么一个月的时间做梦都在敲代码。总结《机房收费重构》的道路需要从学习方法&#xff0c;注意问题&#xff0c;知识总结三方面来进行&#xff01; 《机房收费重构》之路的背景&#xff1a; 1.设计模式&#xff1a; 设计模式的思…

带有Python示例的math.exp()方法

Python math.exp()方法 (Python math.exp() method) math.exp() method is a library method of math module, it is used to get the number in exponential form, it accepts a number and returns the number in the exponential format (if the number is x, it returns e*…

想快速拥有个人网站?来试试这个...

一、简介Hugo 是Go语言实现的一款静态网站生成器。它简单、易用、高效、易扩展、快速部署。相比较其他静态网站生成器&#xff0c;它的优点有这几点&#xff1a;项目构建特别快主题目录与站点目录结构一样配置文件为*.toml 格式&#xff0c;语法常简单易懂&#xff0c;没有缩进…

android 类ios actionsheet效果

1.http://blog.csdn.net/zhaoxy_thu/article/details/17733389 2. https://github.com/ojhariddhish/actionsheetdemo

带有Python示例的math.cos()方法

Python math.cos()方法 (Python math.cos() method) math.cos() method is a library method of math module, it is used to get the cosine of the number radians, it accepts a number returns the cosine of the given number radians. math.cos()方法是数学模块的库方法&…

Linux Debian下突然所有命令未找到,显示bash: xxxxx: command not found的解决办法

出现这个问题是因为系统的环境变量没有正确配置造成的。 在终端内执行下面命令&#xff0c;确实可以解决办法&#xff0c;但是当关掉当前的shell窗口或者重启Linux的时候&#xff0c;我就发现又出现了找不到命令的错误。 export PATH/bin:/usr/bin:$PATH下面分享下彻底解决方…

Java对象都是在堆上分配空间吗?答案竟然是...

作者 l Hollis来源 l Hollis&#xff08;ID&#xff1a;hollischuang&#xff09;Java作为一种面向对象的&#xff0c;跨平台语言&#xff0c;其对象、内存等一直是比较难的知识点&#xff0c;所以&#xff0c;即使是一个Java的初学者&#xff0c;也一定或多或少的对JVM有一些了…

c+pow函数的头文件_pow()函数以及C ++中的示例

cpow函数的头文件C pow()函数 (C pow() function) pow() function is a library function of cmath header (<math.h> in earlier versions), it is used to find the raise to the power, it accepts two arguments and returns the first argument to the power of th…

正则表达式 使用分支

正则表达式是非常有用的文本检索工具&#xff0c;这是一个非常简单而全面的教程正则表达式30分钟入门教程。这一篇教程&#xff0c;可以作为快速学习的工具。正则表达式&#xff0c;可能用的不是非常多&#xff0c;只是在Linux下&#xff0c;分析代码时候&#xff0c;用的比较多…

Python pandas读取csv txt excel和mysql数据库文件方法

# _*_coding:utf-8_*_ import os import pandas as pd import pymysql# Press the green button in the gutter to run the script. if __name__ __main__:strFilePath1 os.getcwd() "/天气.csv"df1 pd.read_csv(strFilePath1)print(df1)strFilePath2 os.getcwd…

服务器运行容器工具大盘点!

服务器到底是什么&#xff1f;服务器的硬件好理解&#xff0c;其实就是一台性能、稳定性、扩展性等等比我们普通个人PC强的一台机器而已&#xff0c;它也需要搭载操作系统&#xff0c;比如有专门的Windows Server或者各种Linux发行版系统。只不过咱这里很多小伙伴可能还是处于学…

python函数示例_使用Python中的示例的input()函数

python函数示例Python input()函数 (Python input() function) input() function is a library function, it is used to get the user input, it shows given message on the console and wait for the input and returns input value in string format. input()函数是一个库函…

Python pandas DateFrame查询数据df.loc的5种方法

# _*_coding:utf-8_*_ import pandas as pd# 自定义函数 def func(df):return df[temperature] < 32# Press the green button in the gutter to run the script. if __name__ __main__:data {date: [2022-08-21, 2022-08-22, 2022-08-23],weather: ["晴", &quo…

想读Spring源码?先从这篇「 极简教程」开始吧...

为什么要阅读源码&#xff1f;这是一个有趣的问题&#xff0c;类似的问题还有&#xff0c;为什么要看书&#xff1f;为什么要爬山&#xff1f; 这也是一个哲学问题&#xff0c;我想每个人都有不同的答案&#xff0c;下面我是对阅读源码好处的一些思考。 &#xff08;PS&#x…

Python pandas DateFrame新增数据列

下面代码在Linux下运行 # _*_coding:utf-8_*_ import os import pandas as pd# 自定义函数 def func(x):if x[temperature] > 30:return 高温if x[temperature] < 10:return 低温return 常温# Press the green button in the gutter to run the script. if __name__ __…

python函数示例_带Python示例的float()函数

python函数示例Python float()函数 (Python float() function) float() function is a library function in Python, it is used to get a float value from a given number or a string. It accepts either a string (that should contain a number) or a number and returns …