是在Java中写入易失性的内存屏障

MKK*_*MKK 22 java concurrency volatile jls

我最近在一次演讲中听到,写入volatile会触发线程写入的每个变量的内存屏障.这是真的正确吗?从JLS看来,似乎只有相关的变量才被刷新,而其他变量则没有.有人知道什么是正确的吗?能指出我在JLS的具体位置吗?

Joh*_*int 22

是的,它会引发障碍.你可以在这里阅读更多.LoadLoad LoadStore StoreStore StoreLoad有4种类型.

至于你的问题

从JLS看来,似乎只有相关的变量才被刷新,而其他变量则没有.有人知道什么是正确的吗?

在易失性存储之前发生的所有写入都可由任何其他线程看到,其他线程加载此新存储.但是,如果不加载新值,则在易失性加载之前发生的写入可能会或可能不会被其他线程看到.

一个实际的例子

volatile int a =0;
int b = 0;

Thread-1
b = 10;
a = 3;

Thread-2
if(a == 0){
  // b can b 10 or 0
} 
if(a == 3){
   // b is guaranteed to be 10 (according to the JMM)
}
Run Code Online (Sandbox Code Playgroud)

  • @PhilippWendler我认为重点是:如果a == 0,写'a = 3`还没有发生,b可以是任何东西,包括10.如果a == 3,写'a = 3`已经发生了你保证有b == 10. (12认同)
  • 我认为你的例子是错的.Thread-2不需要将`a`与3比较来查看`b`的新值,只需读取'a`即可.因此,在第一行之后,Thread-2保证看到`b`等于10(在所有分支中).但是,从不读取"a"的第三个线程不能保证看到"b"的新值. (4认同)
  • +1 *“使用其他线程加载这个新存储的谓词”* (2认同)

MKK*_*MKK 5

对挥发性变量和其他变量的引用是正确的。我没有意识到happens-before的传递性是VM必须实现的东西,而不是从定义中得出的东西。我仍然感到困惑,为什么具有如此深远影响的事情没有明确说明,而实际上是某种定义的推论。总结一下:假设您有 4 个这样的操作:

thread1        thread2
a1
a2
                a3
                a4
Run Code Online (Sandbox Code Playgroud)

其中a2是对易失性变量v的写入,a3是对同一个易失性变量v的读取。根据happens-before (hb)的定义可以得出hb(a1,a2)和hb(a3,a4)。另外,对于挥发物,我们有 hb(a2,a3)。现在根据 hb 所需的传递性得出 hb(a1,a3)。因此,易失性变量 v 的写入和后续读取起到了内存屏障的作用。