2015-09-06 76 views
0

假設線程A和B中運行Java代碼的next功能字數:的Java:有兩個線程

class Test 
{ 
    int sum=0; 

    void next() 
    { 
     for(int i=0;i<100;i++) 
      sum++; 
    } 
} 

什麼可能是sum值當兩個線程完成未來運行? 我的回答是100-200。但是,我錯過了一些情況。

總和的值是否小於100?

+3

將不會有任何合理的價值期待,因爲數據的比賽是不確定的行爲。 –

+0

在競爭條件下,「總和」被**損壞**按位**。 –

+0

什麼是**語言**?!...一般來說,增加一個變量值是** NOT **是一個線程安全的操作,所以當值被複制到/從寄存器之前,您可以最終得到未定義的結果/在另一個線程已經完成其部分操作之後。許多語言在.Net中都有「聯鎖增量」操作:[Interlocked.Increment](https://msdn.microsoft.com/en-us/library/dd78zt0c(v = vs.110).aspx)。 –

回答

2

正如其他人之前所說(包括你自己),++增量操作符(前綴和後綴)是非原子的,因此你的代碼不是線程安全的。 Java Language Specification描述了在執行「Postfix Increment Operator ++」期間在幕後發生的事情。最相關的部分是,它包括3個步驟:

  1. 讀變量
  2. 遞增
  3. 寫入新值返回到可變

由於(1)和(3 )是int的原子,不能有任何按位損壞。步驟(2)本身也完全正常工作,因爲它專門用於線程本地數據。

因此,我們留下的唯一可能的問題是經典的Lost Update Problem

由於丟失的更新只能減少結果,我同意你的200的上限。如果代碼在增量操作實際上是是原子操作的平臺上運行(Java語言規範不是禁止這種情況),或者如果所有線程在完全增量完成後發生巧合切換,或者如果一個線程沒有被安排到另一個線程完成。

現在,我會把比你低的下限。您對100的猜測可能基於這樣的想法:爲了丟失一次更新,我們需要另一次更新來覆蓋它。所以我們永遠不會失去一半以上的增量。但其實這是錯誤的,因爲一個線程的增量可消滅其他線程的幾個增量,見交織這個例子:

Thread A     || Thread B 
===================================================== 
read sum (0)    || 
         || read sum(0) 
         || increment sum locally(1) 
         || write sum(1) 
         || read sum(1) 
         || increment sum locally(2) 
         || write sum(2) 
         || read sum(2) 
         || increment sum locally(3) 
         || write sum(3) 
increment sum locally(1) || 
write sum(1)    || 

然而,爲了消滅一個線程的增量,我們需要在另一個線程中至少有一個成功增量。由於兩個線程的增量必須被殲滅以獲得最低的結果(否則我們將至少有100個成功的增量),所以我們需要兩個線程中至少一次成功更新,因此總共有2個成功的增量。

這個最小值可以通過例如用下面的交織:

Thread A     || Thread B 
======================================================== 
read sum (0)    || 
          || read sum (0) 
          || increment sum locally (1) 
          || write sum (1) 
          || read sum (1) 
          || increment sum locally (2) 
          || write sum (2) 
          || read sum (2) 
          || increment sum locally (3) 
          || write sum (3) 
          || 
          || ... 95 more increments ... 
          || 
          || read sum (98) 
          || increment sum locally (99) 
          || write sum (99) 
increment sum locally(1) || 
write sum (1)    || 
          || read sum (1) 
read sum (1)    || 
increment sum locally(2) || 
write sum (2)    || 
read sum (2)    || 
increment sum locally(3) || 
write sum (3)    || 
read sum (3)    || 
increment sum locally(4) || 
write sum (4)    || 
          || 
... 95 more increments ... || 
          || 
read sum (99)    || 
increment sum locally(100) || 
write sum (100)   || 
          || increment sum locally (2) 
          || write sum (2) 

因此,答案是:的值可以是2級的線程2和200之間。

+0

好吧,我明白我的錯誤。非常好,內容翔實的答案! – Idan

0

我的答案是最小1和最大100。

增量代碼如下所示:
1- MOV AX,MEM [總和]
2-INC斧
3- MOV MEM [總和],斧

現在讓運行與用於示例(int i = 0; i < 3; i ++)

T1 line 1 | ==> ax = 0; MEM [總和] = 0;
T1 line 2 | ==> ax = 1; MEM [總和] = 0;
T2 line 1 | ==> ax = 0; MEM [總和] = 0;
T1 line 3 | ==> ax = 0; MEM [總和] = 0;
T2 line 2 | ==> ax = 1; MEM [總和] = 0;
T1 line 1 | ==> ax = 0; MEM [總和] = 0;
T2 line 3 | ==> ax = 0; MEM [總和] = 0;
T1 line 2 | ==> ax = 1; MEM [總和] = 0;
T2 line 1 | ==> ax = 0; MEM [總和] = 0;
T1 line 3 | ==> ax = 0; MEM [總和] = 0;
T2 line 2 | ==> ax = 1; MEM [總和] = 0;
T1 line 1 | ==> ax = 0; MEM [總和] = 0;
T2 line 3 | ==> ax = 0; MEM [總和] = 0;
T1 line 2 | ==> ax = 1; MEM [總和] = 0;
T2 line 1 | ==> ax = 0; MEM [總和] = 0;
T1 line 3 | ==> ax = 0; MEM [總和] = 0;
T2 line 2 | ==> ax = 1; MEM [總和] = 0;
T2 line 3 | ==> ax = 1; MEM [總和] = 1;

這同樣適用於I = 100