为什么这个java代码中没有竞争条件?

时间:2014-09-01 00:17:03

标签: java multithreading parallel-processing

这是我的主题:

public class MyRunnable implements Runnable
{
  public static int num = 0;

  private void add()
  {
    num = num + 1;
  }

  @Override
  public void run()
  {
    for (int i=0;i<10000;i++)
    {
      add();
      System.out.println(num);
    }
  }
}

以下是我的主要内容:

public class MultiThread
{
  public static void main(String[] argv)
  {
    Thread mt1 = new Thread(new MyRunnable(), "A");
    Thread mt2 = new Thread(new MyRunnable(), "B");

    mt1.start();
    mt2.start();
  }

}

我希望看到那里的竞争条件,因此输出应该小于20000.但是,我得到的实际输出是:

19975
19976
19977
19978
19979
19980
19981
19982
19983
19984
19985
19986
19987
19988
19989
19990
19991
19992
19993
19994
19995
19996
19997
19998
19999
20000

Process finished with exit code 0

有人可以向我解释为什么在这个java程序中,即使我没有进行任何锁定或同步,添加操作似乎也是原子的?

3 个答案:

答案 0 :(得分:3)

你没有尝试过足够的时间,或者没有仔细研究过你的结果。这段代码

private void add()
{
    num = num + 1;
}

绝不安全。你正在为失去的更新做好准备。两个线程都将读取num的相同值,并且每个都将更新它,因此一个增量将丢失。

答案 1 :(得分:1)

第一个线程很可能在第二个线程开始自己的循环之前完成其(非常短的)循环,因此它们似乎不会相互干扰。

如果你尝试更长的循环或在循环中添加延迟,你会看到预期的行为。

答案 2 :(得分:0)

在你提出的情况下,更明确地了解种族状况的概念;问题当然是区域

  

num = num + 1;

为了澄清为什么这是非常不安全并且会导致竞争条件,您需要查看定义它的assembly code。现在,在Java中,您可能认为这只是 正在执行的一行代码,但这远非事实。让我解释一下......

考虑以下装配线

LOAD    @i, r0    ;load the value of 'i' into a register from memory
ADD     r0, 1     ;increment the value in the register
STORE   r0, @i    ;write the updated value back to memory

或简单来说:

Fetch i into a register
Increment the register
Write it back to i

当线程A将i提取到寄存器中,增加它然后在写回之前发生竞争条件 - 线程B进入并执行相同的操作 - 获取&amp;增量(哦不)。它可以在3行代码中的任何地方

因此,此代码被视为不安全,可能导致竞争条件。它不会在第1000次发生的事实并不意味着它不会发生。