2015-09-06 53 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