Java volatile是否读取刷新写入,并执行易失性写入更新读取

时间:2016-03-04 23:53:51

标签: java multithreading

我理解read-acquire(在它之后没有重新排序后续的读/写操作)和write-release(不重新排序它之前的读/写操作)。 我的q是: -

  1. 如果是read-acquire,它之前的写操作是否会被刷新?
  2. 如果是写入释放,先前的读取是否会更新?
  3. 此外,read-acquire与volatile读取相同,而write release与Java中的volatile write相同吗?

    为什么这很重要的是,让我们来看一下写新版本。

    y = x; // a read.. let's say x is 1 at this point
    System.out.println(y);// 1 printed
    //or you can also consider System.out.println(x);
    write_release_barrier();
    //somewhere here, some thread sets x = 2
    ready = true;// this is volatile
    System.out.println(y);// or maybe, println(x).. what will be printed?
    

    此时,是x 2还是1? 在这里,考虑准备挥发。 据我所知,volatile之前的所有商店都将首先显示出来......然后只有volatile才能显示出来。感谢。

    参考: - http://preshing.com/20120913/acquire-and-release-semantics/

4 个答案:

答案 0 :(得分:11)

否:并非所有写入都被刷新,所有读取也都没有更新。

Java工作在多线程的“先发生”基础上。基本上,如果A发生在B之前,而B发生在C之前,那么A发生在C之前。所以你的问题等于x=2是否正式发生 - 在一些读取x的行为之前。

发生前 - 边缘基本上是通过同步与关系建立的,这些关系在JLS 17.4.4中定义。有几种不同的方法可以做到这一点,但对于挥发性物质来说,它基本上等于对易失性事件的写入 - 在读取相同的易失性之前:

  
      
  • 写入易失性变量v(§8.3.1.4)通过任何线程(其中“后续”根据同步顺序定义)与v的所有后续读取同步 -
  •   

鉴于此,如果您的线程写入ready = true,那么单独写入并不意味着任何发生在它之前(就该写入而言)。实际上恰恰相反;写入ready的事情发生在其他线程的事情之前,如果他们阅读ready

所以,如果另一个线程(设置x = 2)在之后写入,它设置x = 2,并且此线程(您在上面发布的)然后阅读ready,然后会看到x = 2。这是因为写入发生在读取之前,因此读取线程会看到写入线程已完成的所有操作(直到并包括写入)。否则,你有数据竞争,基本上所有的赌注都已关闭。

另外几点说明:

  • 如果您没有事先发生的边缘,可能仍会看到更新;只是你不能保证。所以,不要假设如果你没有读到ready的写入,那么你仍然会看到x = 1。您可能会看到x = 1或x = 2,或者可能是其他一些写入(最多包括x = 0的默认值)
  • 在您的示例中,y始终为1,因为您不会在“此处某处”评论后重新阅读x。出于这个答案的目的,我假设在y=x之前或之后有第二个ready = true行。如果没有,则y的值将与第一个println中的值保持不变(假设没有其他线程直接更改它 - 如果它是局部变量则保证),因为线程内的操作总是出现好像他们没有重新订购。

答案 1 :(得分:7)

Java内存模型未按" read-acquire"指定。和#34;写入释放"。这些术语/概念来自其他背景,正如您引用的文章非常清楚,它们经常被(由不同的专家)用来表示不同的事物。

如果你想了解挥发性物质在Java中是如何工作的,你需要了解Java内存模型和Java术语......(幸运的是)有充分理由且精确指定 1 。尝试将Java内存模型映射到" read-acquire"和#34;写入释放"语义是一个坏主意,因为:

  • "只读获取"和#34;写入释放"术语和语义没有明确规定,

  • 假设的JMM - > "只读获取" /"写入释放"语义映射只是JMM的一种可能实现。其他映射可能存在具有不同且同样有效的语义

1 - ...模块专家已经注意到某些版本的JMM存在缺陷。但重点是,已经做出了认真的尝试,提供理论上合理的规范......作为Java语言规范的一部分。

答案 2 :(得分:5)

不,读取volatile变量不会刷新先前的写入。可见操作将确保前面的操作可见,但读取volatile变量对其他线程不可见。

不,写入易失性变量不会清除先前读取值的缓存。它只能保证刷新以前的写入。

在您的示例中,显然y在最后一行仍然是1。根据前面的输出,只对y进行了一次分配,那是1。也许这是一个错字,你打算写println(x),但即使这样,2的值也不能保证可见。

答案 3 :(得分:1)

对于您的第一个问题,答案是FIFO订单

关于第二个问题:请检查Volatile Vs Static in java