在 system.exit() 之后线程仍在运行

Posted

技术标签:

【中文标题】在 system.exit() 之后线程仍在运行【英文标题】:threads still running after system.exit() 【发布时间】:2018-07-12 21:37:51 【问题描述】:

我正在学习 Java 中的多线程,并且正在尝试使用线程。我想出了一种赛车条件代码(有点……原谅这个菜鸟),其中 2 个线程循环工作(100 次)并竞相更改静态变量。目标是当线程首先完成循环(在时间片环境中运行或并行运行)时,打印变量的值并调用 system.exit(int status) 来终止程序!这是为了找出哪个线程赢得了比赛!

预期的输出应该是:nameOfTheThreadThatWonTheRace + "--" + valueOf'I'IncrementedInTheLoop

代码如下:

public class Nest

  public static void main(String[] args) throws Exception
  
    Thread r1 = new Thread(new Racer(),"R1");
    Thread r2 = new Thread(new Racer(),"R2");
    r1.start();
    r2.start();
  


class Racer implements Runnable

  public void run()
  
    for(int i = 0; i <= 100; i++)
    
      Value.value = Thread.currentThread().getName() + "--" + i;
    
    Value.printValue();
    System.exit(0);
  


class Value

  static String value = null;

  public static void printValue()
  
    System.out.println(value);
  

但实际输出不同(4 次运行):

    R2--100 R2--100

    R2--84 R2--100

    R1--100 R1--100

    R2--39 R2--100

我不知道为什么在任何一个线程到达“System.exit(0)”行后 JVM 没有停止? exit() 是否只关闭主线程或整个 JVM 还是仍在执行的线程正在阻止 JVM 停止?

另外请解释一下为什么要生产2行o/p?

For extra info:
Processor--> Intel® Core™ i5-7200U CPU @ 2.50GHz × 4 
Ram--> 8GB
OS--> Fed27 workstation

(我还没有接触过 java.util.concurrent API,但我知道我可以在那里以不同的方式管理线程..)

如果您能用普通的线程术语解释而不是参考并发 API,我将不胜感激。

感谢您的帮助,再次原谅这个菜鸟:)

【问题讨论】:

我没有阅读所有代码,但它完全是线程不安全,所以......你遇到的任何惊喜都是因为这个。阅读一些关于多线程编程的教程,你会没事的。 我知道它不是线程安全的......这实际上是预期的行为(我猜)但我想知道为什么 System.exit() 不起作用...... 您能否再解释一下并提及您期望的确切输出?实际输出有什么问题?根据您的程序,它们似乎是正确的。 System.exit() 工作正常。是你的代码被破坏了,你的期望是错误的,所以对它的输出感到疑惑并不是很有用。 我期望的 o/p 是首先完成循环的线程应该打印出“值”,并且无论其他线程如何,程序都应该在该点终止......如果我的程序是错了......你能帮我纠正它以获得预期的结果吗?否则任何其他指针都会有帮助 【参考方案1】:

处理System.exit(0) 需要时间,因此当调用它的线程等待操作系统杀死并回收运行在不同内核上的代码时,运行在不同内核上的代码可以完成。这使得看起来两个线程都“赢得了比赛”。

请记住,在多线程环境中,一个线程无法明确控制另一个线程的时间;甚至在系统关闭时也没有。如果您想要这种控制,您需要将其显式写入程序中(例如让两个线程检查线程安全标志,以确定比赛是否仍在运行)。

【讨论】:

谢谢...我想我明白了您所说的...所以即使代码是一个核心被杀死...其他代码中的剩余线程,因为它们仍在运行可以停止JVM 从停止...它做对了吗? @shashikanthchillappagari 其他线程不会阻止JVM停止,它们只是在一个线程调用System.exit(0)之后,操作系统强制终止没有调用@987654323的线程之前完成@。 System.exit(0) 也需要时间运行!当然,您的程序还有其他问题,但假设在调用System.exit(0) 的那一刻,JVM 已从所有内核中完全删除,这并不是事情的运作方式。调用System.exit(0) 的线程向操作系统发出请求,然后由操作系统处理它,这需要时间。【参考方案2】:

我认为,如果您在程序中添加一点日志记录,那么它可以帮助您了解真正发生的事情:

    public void run()
      
        for(int i = 0; i <= 100; i++)
        
        if(i==100)
        
            System.out.println(Thread.currentThread().getName() +" : before setting value 100th time : " + System.currentTimeMillis());
        
          Value.value = Thread.currentThread().getName() + "--" + i;
          if(i==100)
        
            System.out.println(Thread.currentThread().getName() +" : after setting value 100th time : " + System.currentTimeMillis());
        
        
        System.out.println(Thread.currentThread().getName() +" : before printing : " + System.currentTimeMillis());
        Value.printValue();
        System.out.println(Thread.currentThread().getName() +" : before exiting : " + System.currentTimeMillis());
        System.exit(0);
      


class Value

  static String value = null;

  public static void printValue()
  
    System.out.println(Thread.currentThread().getName() + " : value : " + value + " : " + System.currentTimeMillis());
  

首先,如果任何线程调用 System.exit(),那么 JVM 就会终止(杀死所有线程!!!)。它不会等待任何线程执行,但会调用关闭挂钩、未调用的终结器等。因此,JVM 可能需要一些时间才能完成。你可以在这里阅读更多:

https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exit(int)

现在,您只提到了 4 次运行的输出。如果你再运行几次,我相信你会得到以下输出:

R2--100

这显然意味着 JVM 在其他线程执行之前就终止了。

现在你的输出,说

R2--100 R2--100

在 JVM 可以终止之前执行的两个线程,简单!

需要注意的一点是,两个线程的名称都是 R2。这是因为 value 变量是静态的并且在 2 个线程之间共享。由于竞争条件,在 R2 可以打印该值之前,它已由 R1 设置。如果您添加我建议的日志记录,您可以看到这一点。

请看下面的示例输出:

R2 : before setting value 100th time : 1517579707689
R2 : after setting value 100th time : 1517579707690
R1 : before setting value 100th time : 1517579707689
R1 : after setting value 100th time : 1517579707691
R2 : before printing : 1517579707691
R1 : before printing : 1517579707692
R2 : value : R1--100
R1 : value : R1--100
R2 : before exiting : 1517579707694
R1 : before exiting : 1517579707694

【讨论】:

这消除了我的疑虑......谢谢......我又运行了几次,得到了单一的输出结果......感谢分享这个【参考方案3】:

调用 System.exit() 将关闭 JVM(请参阅 javadocs 或 language specs)。

两个线程并行更新 Value 的静态成员。这种不安全更新的结果没有定义。这就是为什么您会看到 strage 输出。

解决方案是在 Racer 类中使用局部变量或字段,因为您不必在两个线程之间共享数据。此外,您甚至不需要它,因为您可以使用 Thread.currentThread().getName() 获取当前线程的名称

【讨论】:

以上是关于在 system.exit() 之后线程仍在运行的主要内容,如果未能解决你的问题,请参考以下文章

为啥即使在 System.exit(0) 之后也需要返回;

关于JUnit4无法支持多线程测试的解决方法

java 多线程 System.exit; 是退出当前线程,还是结束整个虚拟机

Java:如何测试调用 System.exit() 的方法?

在 System.exit(0) 处解除绑定

QThread:线程仍在运行时被销毁?