我正在学习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)&#39;之后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&#39;但是我知道我可以用不同的方式管理线程..)
如果您能够以正常的线程术语解释而不是引用Concurrent API,我将不胜感激。
再次感谢您的帮助和原谅这个菜鸟:)
答案 0 :(得分:1)
处理System.exit(0)
需要时间,因此当调用它的线程正在等待操作系统终止并回收在不同内核上运行的代码时,在不同内核上运行的代码可以完成。这使得它看起来像两个线程“赢得了比赛”。
请记住,在多线程环境中,一个线程没有明确控制另一个线程的时序;甚至没有在系统关机。如果你想要那种控制,你需要明确地将它写入程序(例如让两个线程都检查一个线程安全标志,以确定是否仍在运行竞争)。
答案 1 :(得分:0)
调用System.exit()将关闭JVM(请参阅javadocs或language specs)。
两个线程并行更新Value的静态成员。没有定义这种不安全更新的结果。这就是你看到strage输出的原因。
解决方案是在Racer类中使用局部变量或字段,因为您不必在两个线程之间共享数据。此外,您甚至不需要它,因为您可以使用Thread.currentThread().getName()获取当前主题的名称
答案 2 :(得分: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终止(终止所有线程!!!)。它不会等待任何线程执行,但它会调用shutdown hooks,uninvoked finalizers等。因此,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。这是因为值变量是静态的并且在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