当前位置:   article > 正文

Java中如何终止运行中的线程?死循环是如何跳出来的?_接口死循环了如何终止线程

接口死循环了如何终止线程

Java中 终止线程通常意味着在线程处理任务完成之前停掉正在做的操作,也就是放弃当前的操作。

在 Java 中有以下 3 种方法可以终止正在运行的线程:

  • 使用退出标志,使线程正常退出,也就是当 run() 方法完成后线程中止。
  • 使用stop()方法,已被弃用。原因是:stop()是立即终止,会导致一些数据被到处理一部分就会被终止,而用户并不知道哪些数据被处理,哪些没有被处理,产生了不完整的“残疾”数据,不符合完整性,所以被废弃。So, forget it!
  • 使用 interrupt 方法中断线程。注意使用interrupt()方法中断正在运行中的线程只会修改中断状态位,可以通过isInterrupted()判断。如果使用interrupt()方法中断阻塞中的线程,那么就会抛出InterruptedException异常,可以通过catch捕获异常,然后进行处理后终止线程。有些情况,我们不能判断线程的状态,所以使用interrupt()方法时一定要慎重考虑。

使用volatile标志位

看一个简单的例子:

首先,实现一个Runnable接口,在其中定义volatile标志位,在run()方法中使用标志位控制程序运行。

  1. public class MyRunnable implements Runnable {
  2. //定义退出标志,true会一直执行,false会退出循环
  3. //使用volatile目的是保证可见性,一处修改了标志,处处都要去主存读取新的值,而不是使用缓存
  4. public volatile boolean flag = true;
  5. public void run() {
  6. System.out.println("第" + Thread.currentThread().getName() + "个线程创建");
  7. try {
  8. Thread.sleep(1000L);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. //退出标志生效位置
  13. while (flag) {
  14. }
  15. System.out.println("第" + Thread.currentThread().getName() + "个线程终止");
  16. }
  17. }

然后,在main()方法中创建线程,在合适的时候,修改标志位,终止运行中的线程。

  1. public class TreadTest {
  2. public static void main(String[] arg) throws InterruptedException {
  3. MyRunnable runnable = new MyRunnable();
  4. //创建4个线程
  5. for (int i = 0; i < 4; i++) {
  6. Thread thread=new Thread(myRunable,i+" ");
  7. thread.start();
  8. }
  9. //线程休眠
  10. Thread.sleep(2000L);
  11. System.out.println("——————————————————————————");
  12. //修改退出标志,使线程终止
  13. runnable.flag = false;
  14. }
  15. }

最后,运行结果,如下:

使用interrupt()中断的方式

需要明确的一点的是:interrupt() 方法并不像在 for 循环语句中使用 break 语句那样干脆,马上就停止循环。调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。

也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定。这一点很重要,如果中断后,线程立即无条件退出,那么我们又会遇到stop()方法的老问题。

事实上,如果一个线程不能被 interrupt,那么 stop 方法也不会起作用。

我们来看一个使用 interrupt() 的例子:

  1. public class InterruptThread1 extends Thread{
  2. public static void main(String[] args) {
  3. try {
  4. InterruptThread1 t = new InterruptThread1();
  5. t.start();
  6. Thread.sleep(200);
  7. t.interrupt();
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. @Override
  13. public void run() {
  14. super.run();
  15. for(int i = 0; i <= 200000; i++) {
  16. System.out.println("i=" + i);
  17. }
  18. }
  19. }

从输出的结果我们会发现 interrupt 方法并没有停止线程 t 中的处理逻辑,也就是说即使 t 线程被设置为了中断状态,但是这个中断并不会起作用,那么该如何停止线程呢?

这就需要使用到另外两个与线程中断有关的方法了:

  1. public boolean Thread.isInterrupted() //判断是否被中断
  2. public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态

这两个方法使得当前线程能够感知到是否被中断了(通过检查标志位)。

所以如果希望线程 t 在中断后停止,就必须先判断是否被中断,并为它增加相应的中断处理代码:

  1. @Override
  2. public void run() {
  3. super.run();
  4. for(int i = 0; i <= 200000; i++) {
  5. //判断是否被中断
  6. if(Thread.currentThread().isInterrupted()){
  7. //处理中断逻辑
  8. break;
  9. }
  10. System.out.println("i=" + i);
  11. }
  12. }

输出结果,for 循环在执行完成前就提前结束了:

在上面这段代码中,我们增加了 Thread.isInterrupted() 来判断当前线程是否被中断了,如果是,则退出 for 循环,结束线程。

这种方式看起来与之前介绍的“使用标志位终止线程”非常类似,但是在遇到 sleep() 或者 wait() 这样的操作,我们只能通过中断来处理了。

public static native void sleep(long millis) throws InterruptedException

Thread.sleep() 方法会抛出一个 InterruptedException 异常,当线程被 sleep() 休眠时,如果被中断,这会就抛出这个异常。

注意:Thread.sleep() 方法由于中断而抛出的异常,是会清除中断标记的。

使用stop()中断

通过查看 JDK 的 API,我们会看到 java.lang.Thread 类型提供了一系列的方法如 start()、stop()、resume()、suspend()、destory()等方法来管理线程。但是除了 start() 之外,其它几个方法都被声名为已过时(deprecated)。

虽然 stop() 方法确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且该方法已被弃用,最好不要使用它。

为什么弃用stop:

调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。

例如,存在一个对象 u 持有 ID 和 NAME 两个字段,假如写入线程在写对象的过程中,只完成了对 ID 的赋值,但没来得及为 NAME 赋值,就被 stop() 导致锁被释放,那么当读取线程得到锁之后再去读取对象 u 的 ID 和 Name 时,就会出现数据不一致的问题,如下图:

while(true){i++;}死循环是如何跳出的

  1. @Override
  2. public void run() {
  3. while (true){
  4. try {
  5. /**
  6. * mBlockingQueue会在线程内声明如下
  7. * private final BlockingQueue<String> mBlockingQueue;
  8. * 线程初始化时会对mBlockingQueue赋值
  9. * */
  10. String string = mBlockingQueue.take();
  11. Log.d("wuyazhouHttp","string: "+string);
  12. } catch (InterruptedException e) {//捕捉异常
  13. if (mQuit) {
  14. Log.d("wuyazhouHttp","线程结束");
  15. return;
  16. }
  17. Log.d("wuyazhouHttp","string: 阻塞列表为空");
  18. continue;
  19. }
  20. }
  21. }
  1. public void quit() {
  2. mQuit = true;//更新标志位
  3. interrupt();//调用interrupt()方法中断阻塞并抛出一个异常
  4. }

首先会更新标志位可以结束线程,其次调用interrupt()方法中断阻塞并抛出一个异常,然后就能调用第二段代码中的return以终止进程

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/1004223
推荐阅读
相关标签
  

闽ICP备14008679号