关于java:为什么不推荐使用-stopsuspend-方法中断线程

30次阅读

共计 5632 个字符,预计需要花费 15 分钟才能阅读完成。

咱们晓得像 stop、suspend 这几种中断或者阻塞线程的办法在较高 java 版本中曾经被标记上了 @Deprecated 过期标签,那么为什么她们已经登上了 java 的历史舞台而又慢慢的推出了舞台呢?

到底是兽性的扭曲还是道德的沦丧呢,亦或是她们不思进取被取而代之呢,如果是被取而代之,那么取而代之的又是何方人也,本文咱们将一探到底。

一、stop 的闭幕

首先 stop 办法的作用是什么呢,用 java 源码中的一句正文来理解一下:Forces the thread to stop executing.,即强制线程进行执行,’Forces’仿佛曾经透漏出了 stop 办法的蛮狠无理。

那么咱们再看看 java 开发者是怎们解释 stop 被淘汰了的:

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 uncheckedThreadDeath 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 stop 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 interrupt method should be used to interrupt the wait.

咱们从中能够看出以下几点:

  1. stop 这种办法实质上是不平安的
  2. 应用 Thread.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 开发者的正文中窥探到一种解决方案:

Many uses of stop 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 interrupt method should be used to interrupt the wait.

能够看到 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 那样暴力的中断线程,会等到以后运行的逻辑完结后再查看是否中断,十分的优雅。

注:运行举例代码可能不会打印出数字,这是因为 t1 线程运行到 while(!Thread.currentThread().isInterrupted()) 时,主线程曾经调了 interrupt 办法,因而屡次运行可能会打印出数字。

二、suspend 的闭幕

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

This method has been deprecated, as it is inherently deadlock-prone. If the target thread holds a lock on the monitor protecting a critical system resource when it is suspended, no thread can access this resource until the target thread is resumed. If the thread that would resume the target thread attempts to lock this monitor prior to calling resume, deadlock results. Such deadlocks typically manifest themselves as“frozen”processes.

从中咱们能够得出以下论断:

  1. suspend 具备人造的死锁偏向
  2. 当某个线程被 suspend 后,该线程持有的锁不会被开释,其余线程也就不能拜访这些资源
  3. suspend 某个线程后,如果在 resume 的过程中出现异常导致 resume 办法执行失败,则 lock 无奈开释,导致死锁

接下来模仿一下由 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 拿捏的死死的,从此整个程序就陷入了无尽的期待中 —- 死锁。

参考:

https://docs.oracle.com/javas…

https://mp.weixin.qq.com/s/G_…

原文链接:https://blog.csdn.net/qq_4040…

版权申明:本文为 CSDN 博主「浪舟子」的原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本申明。

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)

2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0