假設線程A和B中運行Java代碼的next
功能字數:的Java:有兩個線程
class Test
{
int sum=0;
void next()
{
for(int i=0;i<100;i++)
sum++;
}
}
什麼可能是sum
值當兩個線程完成未來運行? 我的回答是100-200。但是,我錯過了一些情況。
總和的值是否小於100?
假設線程A和B中運行Java代碼的next
功能字數:的Java:有兩個線程
class Test
{
int sum=0;
void next()
{
for(int i=0;i<100;i++)
sum++;
}
}
什麼可能是sum
值當兩個線程完成未來運行? 我的回答是100-200。但是,我錯過了一些情況。
總和的值是否小於100?
正如其他人之前所說(包括你自己),++
增量操作符(前綴和後綴)是非原子的,因此你的代碼不是線程安全的。 Java Language Specification描述了在執行「Postfix Increment Operator ++
」期間在幕後發生的事情。最相關的部分是,它包括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之間。
好吧,我明白我的錯誤。非常好,內容翔實的答案! – Idan
我的答案是最小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
將不會有任何合理的價值期待,因爲數據的比賽是不確定的行爲。 –
在競爭條件下,「總和」被**損壞**按位**。 –
什麼是**語言**?!...一般來說,增加一個變量值是** NOT **是一個線程安全的操作,所以當值被複制到/從寄存器之前,您可以最終得到未定義的結果/在另一個線程已經完成其部分操作之後。許多語言在.Net中都有「聯鎖增量」操作:[Interlocked.Increment](https://msdn.microsoft.com/en-us/library/dd78zt0c(v = vs.110).aspx)。 –