重新排序volatile字段周围的正常字段

基于

挥发性有什么作用? http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#incorrectlySync

易变的新保证 http://www.ibm.com/developerworks/library/j-jtp03304/

class VolatileExample { int x = 0; volatile boolean v = false; public void writer() { x = 42; v = true; } public void reader() { if (v == true) { //uses x - guaranteed to see 42. } } } 

看起来。

 1a) write to non-volatile variable x 1b) write to volatile variable v 

1a永远不会移动传球1b

我想知道,如果我将源代码修改为以下内容

 class VolatileExample { int x = 42; volatile boolean v = true; public void writer() { v = false; x = 0; } public void reader() { if (v == true) { //uses x - guaranteed to see 42????? } } } 

是否可以置换以下序列?

 2a) write to volatile variable v 2b) write to non-volatile variable x 

我想知道,2b可以在2a之前移动吗? 这是因为如果2b能够在2a之前移动,读者就不能再保证在if块内看到42。

根据以下信息,我觉得2b可以在2a之前移动。


http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#reordering

写入易失性字段与监视器释放具有相同的记忆效应,从易失性字段读取具有与监视器获取相同的记忆效应。


这意味着在退出同步块之前对线程可见的任何内存操作在进入由同一监视器保护的同步块之后对任何线程都是可见的,因为所有内存操作都在发布之前发生,并且释放发生在获得。


和罗奇汽车旅馆和Java内存模型

 volatile_v = true; <-- monitor release non_volatile_x = 42; (volatile_v will act as a roach motels, and it is fine for non_volatile_x to move into roach motels) non_volatile_x = 42; volatile_v = true; <-- monitor release (volatile_v will act as a roach motels, and it is not OK for non_volatile_x to move out from roach motels) 

我们知道,对于任何先前的读取或写入(从Java5开始),对volatile变量的写入不能重新排序,但反过来却不是这样。 所以将程序重新排序为x = 0; v = false; 根据我的理解,这是正确的。

在我们写入v之后,我们保证在读取v时,在写入v之前发生的每个动作都是可见的,但它没有说明写入v后的动作 – 这些可能发生也可能没发生,甚至可以发生在v写入之前重新排序。

volatile有两个部分,指令顺序和写入“刷新”。

指令的顺序不会改变,但两段代码之间可能有任何延迟,所以在第二种情况下x可能是42但它可能是0.我的猜测是你会经常看到0。

根据您的体系结构,这可能比在其他系统上更有可能,因此在一台计算机上测试它是42,并不意味着它总是在另一台机器上。

据我了解新的内存模型,一旦将volatile值刷新到主内存,刷新线程可以看到的所有其他变量也必须刷新。 但结果是你的2b不能被VM重新排序到2a之前,因为这会违反代码中隐含的’before-before’排序。

我不认为你有这个保证,因为类实例字段x没有封装,并且包可见可以被另一个线程更改。 使x变量变为volatile将解决该问题并保证所有线程的可见性。

语句2b(写入非易失性变量x)可以在语句2a(写入volatile变量v)之前移动,即,另一个线程可能看到v==truex==0 )。

请注意,即使没有重新排序另一个线程,也可能会看到这些值!

假设已创建并初始化VolatileExample的实例。 线程T1在该实例上执行方法reader 。 它看到v == true并被调度程序中断。 第二个线程调用方法writer现在可以执行赋值v=falsex=0 。 当T1恢复并读取x ,它可能会读取值0(但不能保证看到0)。 如果它看到值0,那么我们有这种情况,T1看到v==truex==0 。 对于T1,看起来两个语句都已重新排序。 由于可以在不重新排序的情况下观察到这种状态,因此实际上允许重新排序是有意义的。