Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
考虑一个名为
的简单类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++
可以分解为三个步骤:
c
的当前值。c
。除了第二步递减而不是递增之外,表达式 c--
可以以相同的方式分解。
假设线程 A 调用 increment
,同时线程 B 调用 decrement
。如果 c
的初始值为 0
,则它们的交错操作可能遵循以下顺序:
线程 A 的结果丢失,被线程 B 覆盖。这种特殊的交错只是一种可能性。在不同情况下,可能是线程 B 的结果丢失,或者根本没有错误。因为它们是不可预测的,所以线程干扰错误难以检测和修复。