【问题标题】:If two unsynchronized threads increment a counter X times, can the total result be less than X?如果两个不同步的线程将一个计数器增加 X 次,总的结果会小于 X 吗?
【发布时间】:2019-12-16 16:30:42
【问题描述】:

我在一个紧密循环中有两个不同步的线程,将全局变量递增 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);
    }
}

【问题讨论】:

标签: java multithreading synchronization race-condition data-race


【解决方案1】:

想象一下以下场景:

  • 线程A从global读取初始值0
  • 线程 B 对global 执行 99999 次更新
  • 线程 A 将 1 写入 global
  • 线程 B 从 global 读取 1
  • 线程 A 在 global 上执行其剩余的 99999 次更新
  • 线程 B 将 2 写入 global

然后,两个线程都完成了,但结果值是2,不是2 * 100000,也不是100000

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

换句话说,当global 变量被声明为volatile 时,上面显示的场景甚至是可能的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多