2014-09-01 61 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次並不意味着它不會發生。