-3

我正在学习 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次运行):

  1. R2--100 R2--100

  2. R2--84 R2--100

  3. R1--100 R1--100

  4. 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,我将不胜感激。

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

4

3 回答 3

1

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

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

于 2018-02-02T12:01:11.317 回答
0

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

    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
于 2018-02-02T14:09:12.133 回答
0

调用 System.exit() 将关闭 JVM(请参阅javadocs语言规范)。

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

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

于 2018-02-02T12:07:33.887 回答