好的交互设计网站,做动态效果的插件网站,做网站定金一般多少,虚拟机做网站有用吗今天把小伙伴问懵了#xff0c;小刚#xff0c;你知道怎么停止一个线程吗#xff1f;
这…#xff0c;这…#xff0c;stop#xff1f;
原来平时小刚这小子只知道创建线程#xff0c;不知道怎么暂停线程呀~[狗头] 停止线程是在多线程开发中很重要的技术点#xff0c;…今天把小伙伴问懵了小刚你知道怎么停止一个线程吗
这…这…stop
原来平时小刚这小子只知道创建线程不知道怎么暂停线程呀~[狗头] 停止线程是在多线程开发中很重要的技术点比如在多线程持续处理业务代码时由于处理逻辑中有第三方接口异常我们就假设发送短信接口挂了吧那么此时多线程调用短信接口是没有任何意义的我们希望接口恢复后再对接口进行处理那么此时怎么办呢如何中止已经启动的线程呢
其实在Java中有3种方式可以终止正在运行的线程
使用stop方法强制退出使用stop()方法强制终止线程注意强烈不推荐这种方式并且该方法已经被标记为过期方法了。使用interrupt方法中断线程该方法只是告诉线程要终止但最终何时终止取决于计算机设置标志位使用设置退出标志使线程正常退出也就是当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(输出ii);Thread.sleep(1000);}}catch (InterruptedException e){System.out.println(抛出异常);}}
}执行结果如下
输出i1
输出i2
输出i3
输出i4
输出i5如上我们创建了一个死循环输出的线程ThreadDemo每隔一秒输出i但是当遇到stop()方法后就不再输出了不对看上去没问题呀stop() 方法这不用的好好的吗
嗨怪就怪这个例子太简单了吧我们来看看弄点带操作对象的例子首先创建一个用户实体
public class UserModel {/*** 给定userNamepassword默认值* 用于模拟上一个线程给赋的旧值*/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(输出ii - 线程未停止 );}else{System.out.println(输出ii - 线程已停止 - 抛出异常);throw new InterruptedException();}}}catch (InterruptedException e){System.out.println(线程已结束...);}}
}输出结果
输出i1499992 - 线程未停止
...
输出i1700624 - 线程未停止
输出i1700626 - 线程未停止
输出i1700628 - 线程已停止 - 抛出异常
线程已结束...简单说一下上方代码首先我们创建了一个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() 手动抛异常的方式是目前中断一个正在运行的线程最为正确的方式了。
如果觉得这篇文章对你有用可以左上角关注一下我呀~ 关注我的公众号吧与一万小伙伴一起成长~