我理解read-acquire(在它之后没有重新排序后续的读/写操作)和write-release(不重新排序它之前的读/写操作)。 我的q是: -
此外,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/
答案 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