为什么强烈不推荐使用stop、suspend方法来中断线程?

ByteRiderX
• 阅读 478


我们知道像stop、suspend这几种中断或者阻塞线程的方法在较高java版本中已经被标记上了@Deprecated过期标签,那么为什么她们曾经登上了java的历史舞台而又渐渐的推出了舞台呢,到底是人性的扭曲还是道德的沦丧呢,亦或是她们不思进取被取而代之呢,如果是被取而代之,那么取而代之的又是何方人也,本文我们将一探究竟。

一、stop的落幕
首先stop方法的作用是什么呢,用java源码中的一句注释来了解一下:Forces the thread to stop executing.,即强制线程停止执行,'Forces’似乎已经透漏出了stop方法的蛮狠无理。那么我们再看看java开发者是怎们解释stop被淘汰了的:

我们从中可以看出以下几点:

我们举例来看一下上边提到的两点:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  synchronized (o2)
                  {
                      try {
                          System.out.println("t1获取到锁");
                          Thread.sleep(5000);
                          System.out.println("t1结束");
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
        });
        t1.start();
        Thread.sleep(1000);
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                synchronized (o2)
                {
                    try {
                        System.out.println("t2获取到锁");
                        Thread.sleep(5000);
                        System.out.println("t2结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t2.start();
        t1.stop();
    }

运行结果:

可以看到,当线程t1在获取到o1和o2两个锁开始执行,在还没有执行结束的时候,主线程调用了t1的stop方法中断了t1的执行,释放了t1线程获取到的所有锁,中断后t2获取到了o1和o2锁,开始执行直到结束,而t1却夭折在了sleep的时候,sleep后的代码没有执行。

因此使用stop我们在不知道线程到底运行到了什么地方,暴力的中断了线程,如果sleep后的代码是资源释放、重要业务逻辑等比较重要的代码的话,亦或是其他线程依赖t1线程的运行结果,那直接中断将可能造成很严重的后果。

那么不建议使用stop中断线程我们应该怎么去优雅的结束一个线程呢,我们可以存java开发者的注释中窥探到一种解决方案:

可以看到java开发者推荐我们使用以下两种方法来优雅的停止线程:

1.定义一个变量,由目标线程去不断的检查变量的状态,当变量达到某个状态时停止线程。

代码举例如下:

volatile static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  try {
                      System.out.println("t1获取到锁");
                      while (!flag)
                          Thread.sleep(5000);//执行业务逻辑
                      System.out.println("t1结束");
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
        });
        t1.start();
        Thread.sleep(1000);
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                try {
                    System.out.println("t2获取到锁");
                    Thread.sleep(5000);//执行业务逻辑
                    System.out.println("t2结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
        flag=true;
    }

运行结果:

2.使用interrupt方法中断线程。

代码举例如下:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  System.out.println("t1获取到锁");
                  while (!Thread.currentThread().isInterrupted()) {
                      for (int i = 0; i < 100; i++) {
                          if(i==50)
                              System.out.println();
                          System.out.print(i+" ");
                      }
                      System.out.println();
                  }
                  System.out.println("t1结束");
              }
        });
        t1.start();
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                try {
                    System.out.println("t2获取到锁");
                    Thread.sleep(5000);//执行业务逻辑
                    System.out.println("t2结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
        t1.interrupt();
    }

运行结果:

我们用while (!Thread.currentThread().isInterrupted())来不断判断当前线程是否被中断,中断的话则让线程自然消亡并释放锁。可以看到调用interrupt方法后并不会像stop那样暴力的中断线程,会等到当前运行的逻辑结束后再检查是否中断,非常的优雅。

二、suspend的落幕
suspend方法的作用是挂起某个线程直到调用resume方法来恢复该线程,但是调用了suspend方法后并不会释放被挂起线程获取到的锁,正因如此就给suspend和resume这哥俩贴上了容易引发死锁的标签,当然这也正是导致suspend和resume退出历史舞台的罪魁祸首。同样我们看看java开发者为suspend的淘汰给出的理由:

从中我们可以得出以下结论:

接下来模拟一下由suspend引起的死锁场景,Talk is cheap,show my code:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  System.out.println("t1获取到o1锁开始执行");
                  try {
                      Thread.sleep(5000);//模拟执行业务逻辑
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("t1执行结束");
              }
        });
        t1.start();
        Thread t2=new Thread(()->{
            synchronized (o2)
            {
                System.out.println("t2获取到o2开始执行");
                try {
                    Thread.sleep(2000);//执行耗时业务
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1)
                {
                    System.out.println("t2获取到o1锁开始继续执行");
                }
                System.out.println("t2执行结束");
            }
        });
        t2.start();

        Thread.sleep(1000);
        t1.suspend();
        //假设抛出了一个未知异常
        int i=1/0;
        t1.resume();
    }

运行结果:

可以看到,整个程序卡的死死的,在调用resume恢复t1线程之前抛出了一个未知异常,导致t1一直挂起进而无法释放o1锁,而t2需要获取到o1锁后才能继续执行,但苦苦等待,奈何o1被t1拿捏的死死的,从此整个程序就陷入了无尽的等待中----死锁。

 作者:浪舟子

 blog.csdn.net/qq_40400960/article/details/112651249

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java debug体系为什么不能debug到jdk里所有的代码
作为java码农肯定碰到过当我们debug到一些class的时候,发现当进入到某个方法里是看不到声明的入参名,取而代之的是arg0,arg1等,继续深入更是看不到局部变量,这主要是java类编译的时候没有加g参数导致的,而为什么我们自己在eclipse中写的代码却是可以正常跟踪呢,原因很简单,因为eclipse自行编译的时候是带g参数编译的。  
Wesley13 Wesley13
3年前
java 中断线程的几种方式 interrupt()
中断  中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果。你最好还是牢记以下的几点告诫。  首先,忘掉Thread.stop方法。虽然它确实停止了一个正
御弟哥哥 御弟哥哥
4年前
如何正确停止Java线程,终止Java线程的三种方法
如何正确停止Java线程,终止Java线程的三种方法在Java中有以下3种方法可以终止正在运行的线程:1.使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止。2.使用stop()方法强行终止线程,但不推荐,该方法已被弃用,原因见后文。3.使用interrupt方法中断线程。以下内容翻译自J
Wesley13 Wesley13
3年前
Java并发编程:Lock
一.synchronized的缺陷synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?  在上面一篇文章中,我们了解到如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁
Wesley13 Wesley13
3年前
Java并发系列7
如果要让线程阻塞,我们在讲线程基础的时候已经分析过了。如果要让线程暂停呢,不是blocked,而是waiting,这时候有什么办法?Thread类有一个弃用的方法suspend()是线程暂停的意思,他被弃用的原因是线程挂起的时候并不会释放持有的锁资源,而且suspend()挂起的线程状态依然是runnable,这也是不合理的。那么有没有一种简单的
Wesley13 Wesley13
3年前
Java线程停止方法之Interrupt方法
  最近在学习Java多线程相关的知识点,其中关于线程停止的方法网上也有不少大牛给出了详细的解答,而我这边就其中Interrupt方法的注意点给自己提个醒。  首先还是大概的罗列下停止线程的方法:  1、使用stop()方法等,不过已经不再被推荐使用,和suspend、resume一样。  2、使用退出标志终止线程,引入一个共享变量,volati
Wesley13 Wesley13
3年前
2.Java 并行程序基础
1.初始线程:线程的基本操作1.新建线程2.终止线程stop造成数据不一致3.线程中断publicvoidThread.interrupt()//中断线程publicbooleanThread.isTnterrup
可莉 可莉
3年前
21年了,为什么Java能够持续的受到欢迎呢?为什么一直那么火呢?
!(https://oscimg.oschina.net/oscnet/0810a332bd464c6d8388a0250d70401d.png)Java已经有21年的历史了,甚至更久,而这取决于你所询问的人和你的计算方式。忽略它的年龄不看,Java依然排行第一。它的实用性、性能和向后兼容性都彰显其价值所在。!(https://osc
Wesley13 Wesley13
3年前
JAVA程序设计练习题集答案
一、判断题1.String字符串在创建后可以被修改。(0)2.引用一个类的属性或调用其方法,必须以这个类的对象为前缀。(0final类名)3.当调用一个正在进行线程的stop()方法时,该线程便会进入休眠状态。(0)4.如果一个类声明实现一个接口,但没有实现接口中的所有方法,那么这个类必须是abst
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
从原理聊JVM(四):JVM中的方法调用原理 | 京东云技术团队
多态是Java语言极为重要的一个特性,可以说是Java语言动态性的根本,那么线程执行一个方法时到底在内存中经历了什么,JVM又是如何确定方法执行版本的呢?
ByteRiderX
ByteRiderX
Lv1
我们都不再联系忘了过去的一点一滴
文章
4
粉丝
0
获赞
0