Thread Interference
考虑一个名为Counter的简单类
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
设计Counter
时,每次对increment
的调用将对c
加 1,而对decrement
的每次调用将从c
中减去 1.但是,如果从多个线程引用了Counter
对象,则线程之间的干扰可能会阻止此事件按预期发生。
当在不同线程中运行但作用于相同数据的两个操作交错时,就会发生干扰。这意味着这两个操作由多个步骤组成,并且步骤 Sequences 重叠。
Counter
实例上的操作似乎无法交叉,因为c
上的两个操作都是单个简单语句。但是,即使简单的语句也可以由虚拟机转换为多个步骤。我们不会检查虚拟机采取的具体步骤-足以知道单个表达式c++
可以分解为三个步骤就足够了:
-
检索
c
的当前值。 -
将检索到的值增加 1.
-
将增加的值存储回
c
。
表达式c--
可以用相同的方式分解,除了第二步是递减而不是递增。
假设线程 A 大约在线程 B 调用decrement
的同时调用increment
。如果c
的初始值为0
,则其交错操作可能遵循以下 Sequences:
-
线程 A:检索 c。
-
线程 B:检索 c。
-
线程 A:增加检索值;结果是 1.
-
线程 B:递减检索值;结果是-1.
-
线程 A:将结果存储在 c 中; c 现在是 1.
-
线程 B:将结果存储在 c 中; c 现在为-1.
线程 A 的结果丢失,被线程 B 覆盖。这种特殊的交织只是一种可能。在不同的情况下,可能是线程 B 的结果丢失了,或者根本没有错误。由于线程干扰错误是不可预测的,因此可能很难检测和修复。