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 禁止缓存 i
但 i++
不是原子的。这意味着 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)。
我有一个全局变量
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 禁止缓存 i
但 i++
不是原子的。这意味着 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)。