2014-09-01 62 views
1

这里是我的线程:为什么在这个java代码中没有竞争条件?

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然而,实际输出I得到的是:

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程序的添加操作似乎是原子即使我没有做任何锁定或同步?

+5

结果中没有错误并不意味着没有(可能的)竞争条件。 – 2014-09-01 00:19:22

+0

我跑了很多次这段代码,从来没有得到过比赛条件,但我觉得我没有那么幸运,不会在数十万次迭代中获得竞争条件。 – OneZero 2014-09-01 00:21:30

+0

尝试增加迭代次数(可以说是'1000000'),并将打印语句移到循环之外(将它放在循环之后以增加竞争机会)。 – Pshemo 2014-09-01 00:23:24

回答

3

你只是没有尝试过足够多的时间,或者没有仔细检查你的结果。此段代码

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

是不安全的。你正在设置丢失的更新。两个线程将读取相同的值num,并且每个线程都会更新它,因此一个增量将会丢失。

+3

有可能每个线程花费很多时间做I/O操作,以至于在执行单个加载 - 修改 - 存储所花费的时间相对较少时,它们不会碰到彼此。 – 2014-09-01 00:23:15

+0

@GregHewgill我明白了。删除打印语句后,出现竞争状况。 – OneZero 2014-09-01 00:29:52

+2

@OneZero看看[这个](http://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement)以及'系统的影响。通过out.println()'。 – 2014-09-01 00:31:16

1

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

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

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获取我到寄存器发生争用条件,增加它然后在写回之前 - 线程B进来并执行相同的操作 - 获取&增量(哦否)。它可以在3行代码中的任何地方

所以这段代码被认为是不安全的,并且可能会导致竞争条件。事实上它并没有发生在第1000次并不意味着它不会发生。