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 的结果丢失了,或者根本没有错误。由于线程干扰错误是不可预测的,因此可能很难检测和修复。