我有以下面试问题:
class someClass
{
int sum=0;
public void foo()
{
for(int i=0; i<100; i++)
{
sum++
}
}
}
有两个并行线程在foo方法中运行。 最后的总和值将在100到200之间变化。 问题是为什么。据我所知,只有一个线程获得一个CPU,并且线程在运行时被抢占。在什么时候干扰会使总和达不到200?
答案 0 :(得分:10)
增量不是原子的。它读取值,然后写出递增的值。在这两个操作之间,另一个线程可以以复杂的方式与总和进行交互。
值的范围实际上不是100到200.此范围基于错误的假设,即线程轮流或同时执行每次读取。还有更多可能的交错,其中一些产生明显不同的值。最坏的情况如下(x
表示表达式sum++
中使用的隐式临时表示):
Thread A Thread B
---------------- ----------------
x ← sum (0)
x ← sum (0)
x + 1 → sum (1)
x ← sum (1)
x + 1 → sum (2)
⋮
x ← sum (98)
x + 1 → sum (99)
x + 1 → sum (1)
x ← sum (1)
x ← sum (1)
x + 1 → sum (2)
⋮
x ← sum (99)
x + 1 → sum (100)
x + 1 → sum (2)
因此,最低可能值为2.简单来说,两个线程撤消对方的辛勤工作。你不能低于2,因为线程B不能向线程A提供零 - 它只能输出递增的值 - 并且线程A必须依次递增线程B给它的1。
答案 1 :(得分:3)
第sum++
行是竞争条件。
两个线程都可以读取sum的值为0,然后每个线程将其值递增为1并将该值存回。这意味着sum的值将是1而不是2。
继续这样,你会得到100到200之间的结果。
答案 2 :(得分:0)
现在大多数CPU都有多个内核。因此,如果我们在函数foo()的开头锁定互斥锁并在for循环结束后解锁互斥锁,那么在不同内核上运行的2个线程仍将产生答案200.