Java 围绕易失性字段对正常字段重新排序
基于 volatile做什么 及 对易失性的新保证 看来Java 围绕易失性字段对正常字段重新排序,java,multithreading,Java,Multithreading,基于 volatile做什么 及 对易失性的新保证 看来 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;
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之前移动。
写入易失性字段具有 与显示器具有相同的记忆效果 释放,并从一个易失性 字段与字段具有相同的记忆效果 监视获取
这意味着任何内存操作 之前的线程都可以看到 退出同步的块是 任何线程进入后都可见 受保护的同步块 相同的监视器,因为所有的内存 操作发生在发布之前, 释放发生在 获得
及
volatile_v=true volatile有两个部分,指令顺序和写入的“刷新”
指令的顺序不会改变,但两段代码之间可能存在任何延迟,因此在第二种情况下,x可能是42,但可能是0。我猜你会更经常地看到0
根据您的体系结构,这可能比在其他系统上更可能发生,因此在一台机器上测试发现它是42,并不意味着它将始终在另一台机器上。据我了解,新的内存模型,一旦易失性值刷新到主内存,冲洗线程可以看到的所有其他变量也必须冲洗。但这样做的结果是,虚拟机无法将2b重新排序到2a之前,因为这将违反代码中隐含的“在之前发生”排序。我认为您不能保证这一点,因为类实例字段x没有封装,包可见可以由另一个线程更改。将x变量设置为volatile将解决这个问题,并保证所有线程的可见性。我们知道,对volatile变量的写入不能根据之前的任何读取或写入(从Java5开始)重新排序,但反过来就不是这样了。因此,将程序重新排序为x=0;v=假;据我所知是正确的
在我们写入v之后,我们可以保证在读取v时,在写入v之前发生的每一个动作都是可见的,但它并没有说明写入v之后的任何动作——这些动作可能已经发生,也可能没有发生,甚至可以重新排序以在写入v之前发生。语句2b(写入非易失性变量x)可以移动到语句2a之前(写入易失性变量v),即,另一个线程可能看到v==true
和x==0
)
请注意,即使不重新排序另一个线程,也可能会看到这些值
假设已创建并初始化了VolatileExample
的实例。线程T1在该实例上执行方法reader
。它看到v==true
,并被调度程序中断。调用方法writer
的第二个线程现在可以执行赋值v=false
和x=0
。当T1恢复并读取x
时,它可能读取值0(但不能保证看到0)。如果它看到值0,那么我们就有了这样的情况,T1看到v==true
和x==0
。对于T1来说,这两条语句似乎已被重新排序。由于这种状态可以在不重新排序的情况下观察到,因此实际上允许重新排序是有道理的。为什么说指令顺序不会改变?你有什么具体的参考资料吗?好吧,同样的参考资料基本上说明volatile上的指令不会向前或向后移动。操作顺序并不重要。这是一个单独的操作问题。正如@Peter指出的,调用reader()
的线程可能会进入if块,然后无限期地延迟。当它最终被重新安排执行时,x现在可能是0。你能得到任何具体的例子吗,重新排序可能会发生?这种行为的例子可能非常难以获得(这是问题的本质),但对于第一个声明,我可以引用:-正在使用Volatile修复双重检查锁定的部分。这篇文章的其余部分是基于。未指定在写入易失性变量之前不能对写入进行重新排序,即它应该是有效的。在2a之前移动2b不得违反“发生在”关系<代码>如果一个动作发生在另一个动作之前,那么第一个动作对第二个动作可见并在第二个动作之前排序。
在第二个动作之前不会发生任何动作。因此,将2b移到2a以上应该是好的。
2a) write to volatile variable v
2b) write to non-volatile variable x
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)