Java 并行易失性 i++

Java parallel volatile i++

我有一个全局变量

volatile i = 0;

和两个线程。每个都执行以下操作:

i++;
System.out.print(i);

我收到以下组合。 12、21 和 22。

我明白为什么我得不到 11(volatile 不允许缓存 i)并且我也明白 12 和 22。

我不明白的是怎么可能得到21?

获得此组合的唯一可能方法是稍后打印的线程必须首先将 i 从 0 递增到 1,然后缓存 i==1。然后另一个线程将 i 从 1 递增到 2 然后打印 2。然后第一个线程打印缓存的 i==1。但我认为 volatile 不允许缓存。

编辑:运行 代码 10,000 次后我得到了 11 一次。将 volatile 添加到 i 根本不会改变可能的组合。

markspace 是正确的:volatile 禁止缓存 ii++ 不是原子的。这意味着 i 在递增期间仍然在寄存器中得到某种 "cached"。

r1 = i
//if i changes here r1 does not change
r1 = r1 + 1 
i = r1

这就是11还有可能的原因。 21 是因为 PrintStreams 不同步造成的(参见 Karol Dowbecki 的回答)

您的代码无法保证哪个线程将首先调用 System.out

由于 volatile 关键字,i 的增量和读取按顺序发生,但打印没有。

不幸的是++不是原子操作。尽管 volatile 不允许缓存,但允许 JVM 读取、递增,然后作为单独的操作写入。因此,您尝试实现的概念是行不通的。您需要使用 synchronized 作为它的互斥体,或者使用像 AtomicInteger 这样的东西,它确实提供了一个原子增量操作。

The only possible way...is that the thread that prints later had to be the first to increment i from 0 to 1 and then cached i==1...

您忘记了 System.out.print(i); 的作用:该语句调用 System.out 对象的 print(...) 方法,其中 value 存储在 i 在通话开始的那一刻。

所以这是一种可能发生的情况:

Thread A
increments i  (i now equals 1)
Starts to call `print(1)`  //Notice! that's the digit 1, not the letter i.
gets bogged down somewhere deep in the guts...

          Thread B
          increments i (i=2)
          Calls `print(2)`
          Gets lucky, and the call runs to completion.

Thread A
finishes its `print(1)` call.

两个线程都没有缓存 i 变量。但是,System.out.print(...) 函数对您的 volatile int i 一无所知。它只知道传递给它的 value(1 或 2)。