今天把小伙伴问懵了,小刚,你知道怎么停止一个线程吗?

这…,这…,stop?

原来平时小刚这小子只知道创建线程,不知道怎么暂停线程呀~[狗头]


停止线程是在多线程开发中很重要的技术点,比如在多线程持续处理业务代码时,由于处理逻辑中有第三方接口异常,我们就假设发送短信接口挂了吧,那么此时多线程调用短信接口是没有任何意义的,我们希望接口恢复后再对接口进行处理,那么此时怎么办呢,如何中止已经启动的线程呢?

其实在Java中有3种方式可以终止正在运行的线程:

  1. 使用stop方法强制退出:使用stop()方法强制终止线程,注意,强烈不推荐这种方式,并且该方法已经被标记为过期方法了。
  2. 使用interrupt方法中断线程,该方法只是告诉线程要终止,但最终何时终止取决于计算机;
  3. 设置标志位:使用设置退出标志,使线程正常退出,也就是当run方法完成后线程终止;

尽管罗列了三种方式,但由于存在安全问题,所以舍弃了stop()方法,怎么就安全问题了呢?

暴力停止线程的stop()方法「禁止使用」

之所以说stop()方法暴力是相对于其他两种方式的,只要调用stop()方法,运行中的线程就暂停了,我们通过一段代码测试一下:

public class MyTest {public static void main(String[] args) {try {/**创建线程**/ThreadDemo demo = new ThreadDemo();/**开启线程**/demo.start();/**线程休眠**/Thread.sleep(5000);/**停止线程**/demo.stop();} catch (InterruptedException e) {e.printStackTrace();}}}public class ThreadDemo extends Thread{/**变量i**/private int i = 0;@Overridepublic void run() {try {while (true){i++;System.out.println("输出i:"+i);Thread.sleep(1000);}}catch (InterruptedException e){System.out.println("抛出异常");}}
}

执行结果如下:

输出i:1
输出i:2
输出i:3
输出i:4
输出i:5

如上,我们创建了一个死循环输出的线程ThreadDemo,每隔一秒输出i++,但是当遇到stop()方法后,就不再输出了,不对,看上去没问题呀,stop() 方法这不用的好好的吗?

嗨,怪就怪这个例子太简单了吧,我们来看看弄点带操作对象的例子,首先创建一个用户实体:

public class UserModel {/*** 给定userName+password默认值* 用于模拟上一个线程给赋的旧值*/private String userName = "张三";private String password = "hahahha";/*** 用于复制的方法* 为防止多线程数据错乱,加上synchronized关键字* @param userName* @param password*/synchronized public void setValue(String userName, String password){try {this.userName = userName;Thread.sleep(3000);this.password = password;} catch (InterruptedException e) {e.printStackTrace();}}省略get\set方法...
}

然后我们再在ThreadDemo中使用这个实体:

public class ThreadDemo extends Thread{private UserModel userModel;public ThreadDemo(UserModel userModel){this.userModel = userModel;}@Overridepublic void run() {/*** 重新设置用户名+密码* 用户名:niceyoo* 密码:123456*/userModel.setValue("niceyoo","123456");}
}

然后在MyTest中创建并启动线程,然后调用stop()方法:

public class MyTest {public static void main(String[] args) {try {/**创建用户实体**/UserModel userModel = new UserModel();/**创建线程**/ThreadDemo demo = new ThreadDemo(userModel);/**开启线程**/demo.start();/**线程休眠**/Thread.sleep(1000);/**停止线程**/demo.stop();/**输出用户实体**/System.out.println(userModel.getUserName() + " :" + userModel.getPassword());} catch (ThreadDeath | InterruptedException e) {e.printStackTrace();}}}

输出结果如下:

niceyoo :hahahha

显然跟我们预期的输出结果niceyoo\123456不一致,使用stop()释放锁,对锁定的对象进行了解锁,导致数据得不到同步的处理,出现数据不一致的情况,所以这样就会导致数据安全问题,这也是现在为何 stop() 方法被标注为 “作废、过期”。

interrupted()方法「只告诉要停止,不知道何时停」

使用interrupted()方法就不像是stop()方法那样简单粗暴了,调用该方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程,就好比,我打电话告诉你不要玩游戏了,但是你什么时候停止玩游戏就是你的事了。

public class MyTest {public static void main(String[] args) {try {/**创建线程**/ThreadDemo2 demo = new ThreadDemo2();/**开启线程**/demo.start();/**线程休眠**/Thread.sleep(2000);/**停止线程**/demo.interrupt();} catch (InterruptedException e) {System.out.println("线程已经暂停");e.printStackTrace();}}}public class ThreadDemo2 extends Thread{@Overridepublic void run() {try {for (int i = 0; i < 1800000; i++) {if(!this.isInterrupted()){System.out.println("输出i:"+i++ + " - 线程未停止 ");}else{System.out.println("输出i:"+i++ + " - 线程已停止 - 抛出异常");throw new InterruptedException();}}}catch (InterruptedException e){System.out.println("线程已结束...");}}
}

输出结果:

输出i:1499992 - 线程未停止
...
输出i:1700624 - 线程未停止
输出i:1700626 - 线程未停止
输出i:1700628 - 线程已停止 - 抛出异常
线程已结束...

简单说一下上方代码,首先我们创建了一个for循环输出i++的线程,启动线程后调用 interrupt() 方法停止线程,但是啥时候停止是不可控的,虽然不可控但是还是有方法知道线程是否是停止的,我们在ThreadDemo2线程类中看到 if 判断 — this.isInterrupted() 「等价于Thread.currentThread().isInterrupt() 」,这是用来判断当前线程是否被终止,通过这个判断我们可以做一些业务逻辑处理,通常如果this.isInterrupted被判定为true后,我们会抛一个中断异常,然后通过try-catch捕获。

再额外说一下,有的小伙伴设置的 for 循环变量的最大值比较小,测试执行过程中并没有重现线程被终止,然后就怀疑这个 interrupt() 到底能不能停止线程呀, 不用纠结,这正是线程的自主权,我们无法像 stop() 方法一样直接停止线程的。

设置标志位

设置标志位是用到了共享变量的方式,我们了解线程对于变量的操作都是操作的变量副本,而一旦使用

volatile关键字修饰后,因为其可见性,变量变更始将终从主存中获取最新值。

public class MyTest {public static void main(String[] args) {/**创建2个线程**/ThreadDemo3 demo1 = new ThreadDemo3();ThreadDemo3 demo2 = new ThreadDemo3();demo1.setName("线程1");demo2.setName("线程2");/**开启线程**/demo1.start();demo2.start();/**让线程先运行5s**/try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}/**修改线程的变量**/demo1.heartbeat = false;demo2.heartbeat = false;System.out.println("----暂停线程----");/**让线程再运行5s**/try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}/**再将标志为置为true**/demo1.heartbeat = true;demo2.heartbeat = true;System.out.println("----从新开启线程----");}
}public class ThreadDemo3 extends Thread{/**共享变量**/volatile Boolean heartbeat = true;@Overridepublic void run() {while (true){/**判断标志是否为true**/if (heartbeat){System.out.println("当前运行线程为:" +Thread.currentThread().getName() + " - 运行");}else{System.out.println("当前运行线程为:" +Thread.currentThread().getName() + " - 非运行");}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

输出结果:

省略ing...
当前运行线程为:线程1 - 运行
当前运行线程为:线程2 - 运行
----暂停线程----
省略ing...
当前运行线程为:线程1 - 非运行
当前运行线程为:线程2 - 非运行
----从新开启线程----
当前运行线程为:线程1 - 运行
当前运行线程为:线程2 - 运行
省略ing...

来看一下上方代码,我们在线程类里创建了共享变量heartbeat,因为要监听这个贡献变量的状态,肯定是要用while循环体了,为了演示状态的变更,所以在while循环体代码中没有throw抛出 InterruptedException 异常,正常情况下在判断共享变量为fasle时,也是要手动抛出异常的,ok,这就是设置标志位了。

总结一定要看的

stop()方法在这就不提了,肯定是行不通的,至于为何不能使用大家可以再仔细看看上方那个例子。

然后是interrupt()方法+抛异常处理,看完上边那个例子,大家可能会觉得这个方法有点问题,暂停线程完全靠线程自身决定,即便调用了也不能快速的停止线程,但是我要告诉你,这是目前最为正确的方式… 咳咳,别着急,咱先把设置标志位说了。

设置标志位使用了volatile关键字共享变量方式,通过改变共享变量+抛异常的方式来暂停线程,这个看起来最有效,最正确的方式,其实有一点点问题,而这一点点问题就是为什么让 interrupt() 成为最正确的方式。

volatile标记共享变量方式,在线程发生阻塞时是无法完成响应的。

这个所谓的阻塞指的是什么呢?

其实发生阻塞的情况是比较常见的,比如调用 Thread.join() 方法「当前线程陷入无限期的阻塞,join() 所属的线程对象正常运行run()方法,对join()方法不了解的小伙伴可以去百度了」,或者是 Thread.sleep() 方法,再或者是线程需要等待键盘输入而被阻塞,还有socket网络编程中的 ServerSocket.accept() 方法等等等,总之,在这些种种情况下让线程处于不可运行状态时,即便是主线程修改了共享变量的值,该线程此时根本无法检查循环标志,所以也就无法实现线程中断。

所以,interrupt() + 手动抛异常的方式是目前中断一个正在运行的线程最为正确的方式了。

如果觉得这篇文章对你有用,可以左上角关注一下我呀~

关注我的公众号吧,与一万+小伙伴一起成长~

如何暂停一个正在运行的线程?相关推荐

  1. 腾讯面试官:如何停止一个正在运行的线程?我蒙了。。。

    以下文章来源方志朋的博客,回复"666"获面试宝典 停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作.停止一个线程可以用Thread.stop()方法, ...

  2. 面试官:如何停止一个正在运行的线程?我一脸蒙蔽...

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:cnblogs.com/greta/p/5624839.ht ...

  3. 如何停止一个正在运行的线程?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | cnblogs.com/greta/p/562 ...

  4. 腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。

    停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作.停止一个线程可以用Thread.stop()方法,但最好不要用它.虽然它确实可以停止一个正在运行的线程,但是这个方法是不安 ...

  5. tomcat线程循环异常终止_腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。...

    1. 停止不了的线程 2. 判断线程是否停止状态 3. 能停止的线程--异常法 4. 在沉睡中停止 5. 能停止的线程---暴力停止 6.方法stop()与java.lang.ThreadDeath异 ...

  6. java 如何结束线程_java中,如何安全的结束一个正在运行的线程?

    问题 Java中提供了很多调度线程的方法,上一节介绍了其中一种控制线程的方法:如何等待一个线程结束.那么如果不希望等待线程结束,而是根据问题的需要随时都要中断线程使其结束,这种对线程的控制方法该如何实 ...

  7. linux暂停一个在运行中的进程

    对于一个运行中的进程,我们可以使用kill -STOP pid命令将其暂停执行,使用kill -CONT pid命令恢复其运行. 下面用一个实例说明: 1.首先使用tar命令打包/usr目录: [ro ...

  8. Python 多线程、守护进程、同时运行最大线程数、锁、线程阻塞(线程暂停和继续)

    python 多线程的使用笔记 1.多线程的基本用法 (1)简单任务多线程的开启方式 from threading import Thread import timedef target(name, ...

  9. c如何正常中断一个运行的线程

    最近开发一些东西,线程数非常之多,当用户输入Ctrl+C的情形下,默认的信号处理会把程序退出,这时有可能会有很多线程的资源没有得到很好的释放,造成了内存泄露等等诸如此类的问题,本文就是围绕着这么一个使 ...

最新文章

  1. Codeforces Round #324 (Div. 2) E. Anton and Ira 贪心
  2. Readline-select
  3. LDA (Linear Discriminate Analysis)Fisher Criteria
  4. python爬pdf的曲线_科学网—Python爬PDF - 胡鹏程的博文
  5. 作业调度进程c语言代码,进程调度 时间片轮转调度算法源代码(C语言)
  6. 排名怎么查_常见客户SEO问题解答:网站降权了应该怎么处理?
  7. 什么是服务的熔断降级
  8. php 五子棋源联机版_PHP五子棋服务器代码
  9. 惠普HP OEM XP SP3镜像文件高速下载
  10. 1. ARMv9-A Overview
  11. 适合学生用的蓝牙耳机哪款好?学生党无线蓝牙耳机推荐
  12. ABP Vnext 学习03-授权中心微信小程序登录
  13. 西门子S7系列PLC以太网通讯处理器MPI-131
  14. JAVA 基本数据结构--数组、链表、ArrayList、Linkedlist、hashmap、hashtab等
  15. 数据分区与放置策略解析_数据策略好数据与坏数据
  16. 华为matepad鸿蒙系统,预装鸿蒙系统 华为MatePad Pro 2界面曝光
  17. MATLAB 散点图(scatter)设置点据透明度
  18. python机器学习---数据处理---文本数据处理
  19. Google天涯问答提问遭遇
  20. R语言filter()函数

热门文章

  1. 54失败是成功之母二
  2. [vue] 实际工作中,你总结的vue最佳实践有哪些?
  3. 工作74:vue带参数跳转其他页面
  4. 前端学习(1162):箭头函数面试题
  5. 前端学习(505):垂直居中的第一种方式的优点和缺点
  6. mybatis学习(29):适用于没有自增的数据库
  7. java学习(125):简单异常处理
  8. 基于密度的异常值检测方法整理
  9. ETH—Lwip以太网通信
  10. 用 vue-route 的 beforeEach 实现导航守卫(路由跳转前验证登录)