如果两个不同步的线程将计数器递增 X 次,总结果会小于 X 吗?

Gon*_*n I 4 java multithreading synchronization race-condition data-race

我在紧密循环中有两个不同步的线程,将全局变量递增 X 次 (x=100000)。

全局的正确最终值应该是 2*X,但由于它们不同步,所以它会更少,根据经验,它通常只是略高于 X

然而,在所有测试运行中, global 的值从未低于 X 。

最终结果有可能小于x(小于100000)吗?

public class TestClass {
    static int global;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread( () -> {  for(int i=0; i < 100000; ++i) {     TestClass.global++;  }  });
        Thread t2 = new Thread( () -> { for(int i=0; i < 100000; ++i) {     TestClass.global++;  }  });
        t.start(); t2.start();
        t.join(); t2.join();
        System.out.println("global = " + global);
    }
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 5

想象一下以下场景:

\n\n
    \n
  • 0线程A读取初始值global
  • \n
  • 线程 B 执行 99999 次更新global
  • \n
  • 线程A1写入global
  • \n
  • 线程B1读取global
  • \n
  • 线程 A 执行剩余的 99999 次更新global
  • \n
  • 线程B2写入global
  • \n
\n\n

然后,两个线程都完成,但结果值为2、 not 2 * 100000、nor 100000

\n\n

请注意,上面的示例只是使用了错误的计时,而没有让任何线程感知到其他线程的无序读取或写入(这在没有同步的情况下是允许的),也没有丢失更新(这在这里也是允许的)。

\n\n

换句话说,当global变量被声明时,上面所示的情况甚至是可能的volatile

\n\n

推断读取和写入及其可见性是一个常见的错误,但隐含地假设执行线程\xe2\x80\x99s 代码的特定时间。但不能保证这些线程以相似的指令时序并行运行。

\n\n

但这种情况在您的测试场景中仍然可能发生,因此它们\xe2\x80\x99没有揭示其他可能的行为。此外,某些合法行为可能永远不会发生在特定硬件或特定 JVM 实现上,而开发人员仍然必须对此负责。global += 100000优化器用在此类测试中很少表现出中间值的等效循环替换增量循环可能会很好,但在循环体中插入一些其他重要操作可能会完全改变行为。

\n