的Java:有两个线程
假设线程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)。 –