文档

Java™ 教程-Java Tutorials 中文版
线程干扰
Trail: Essential Classes
Lesson: Concurrency
Section: Synchronization

线程干扰

考虑一个名为 Counter 的简单类


class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

}

Counter 的设计是为了每次调用 increment 都会将 1 加到 c,并且每次调用 decrement 都会从 c 减去 1。但是,如果从多个线程引用 Counter 对象,则线程之间的干扰可能会导致这种情况不按预期发生。

当两个操作在不同的线程中运行但作用于相同的数据时,会发生干扰,interleave (交错)。这意味着这两个操作由多个步骤组成,并且步骤顺序重叠。

对于 Counter 实例的操作似乎不可能出现交错,因为 c 上的两个操作都是单个简单语句。但是,即使是简单的语句也可以由虚拟机转换为多个步骤。我们不会检查虚拟机采取的具体步骤 — 只需要知道单个表达式 c++ 可以分解为三个步骤:

  1. 获取 c 的当前值。
  2. 将获取的值增加 1。
  3. 将递增的值存储回 c

除了第二步递减而不是递增之外,表达式 c-- 可以以相同的方式分解。

假设线程 A 调用 increment,同时线程 B 调用 decrement。如果 c 的初始值为 0,则它们的交错操作可能遵循以下顺序:

  1. 线程 A:获取 c。
  2. 线程 B:获取 c。
  3. 线程 A:增加获取值;结果是 1。
  4. 线程 B:减少获取值;结果是 -1。
  5. 线程 A:将结果存储在 c 中;c 现在是 1。
  6. 线程 B:将结果存储在 c 中;c 现在是 -1。

线程 A 的结果丢失,被线程 B 覆盖。这种特殊的交错只是一种可能性。在不同情况下,可能是线程 B 的结果丢失,或者根本没有错误。因为它们是不可预测的,所以线程干扰错误难以检测和修复。


Previous page: Synchronization
Next page: Memory Consistency Errors