本教程http://tutorials.jenkov.com/java-concurrency/volatile.html说
如果读/写最初发生在对易失性变量的写入之前,则不能在写入易失性变量之后重新排序
从和写入其他变量。对易失性变量的写入之前的读/写保证“发生在”对易失性变量的写入之前。
什么是“在写入易失变量之前”?这是否意味着以前在我们写入易失性变量的方法中进行的读写操作?或者它是一个更大的作用域(也是在调用堆栈的更高的方法中)?
发布于 2021-03-14 12:37:14
JVM可以重新排序操作。例如,如果我们有i
、j
变量和代码
i = 1;
j = 2;
JVM可以以重新排序的方式运行它。
j = 2;
i = 1;
但是,如果标记为j
的volatile
变量,则JVM只运行以下操作
i = 1;
j = 2;
写到i
“发生在写入易失变量”j
之前。
发布于 2021-03-14 16:29:46
JVM确保在任何读取变量之前对易失性变量进行写入。拿两根线。它保证了对于单个线程,执行遵循“如果是串行”的语义.基本上,您可以假设有一个隐式发生-在关系b/w在同一个线程中执行两次之前(编译器仍然可以重新排序指令)。基本上,一个线程有一个总的顺序b/w,它的指令是由发生的-之前的关系琐碎。
多线程程序有许多这样的部分命令(每个线程在本地指令集中都有一个总顺序,但在线程之间没有全局顺序),但是全局指令集没有总订单b/w。同步就是给你的程序尽可能多的总订单。
回到易失性变量,当线程从中读取时,JVM确保所有对其的写入都发生在读取之前。现在,由于这个顺序,写入线程在写入变量之前所做的一切对于从它读取的线程来说都是可见的。因此,要回答您的问题,即使调用堆栈中的变量对读取线程也是可见的。
我试着画一幅直观的图画。这两个线程可以想象为两个并行rails,并且写入一个易失性变量可以是枕木b/w中的一个。你基本上得到了一个
A -----
|
|
------- B
定形总顺序b/w两个线程的执行。由于这个总订单,睡前A中的所有东西都应该是B在轨枕后可见的。
发布于 2021-03-15 10:13:09
JMM是根据关系之前发生的情况来定义的,我们称之为->
。如果是a->b
,那么b
应该可以看到a
的所有内容。这意味着在重新排序加载/存储方面存在限制。
如果a
是一个易失性写入,而b
是相同变量的后续易失性读取,那么a->b
。这称为易失性变量规则。
如果a
发生在代码中的b
之前,那么a->b
。这被称为程序顺序规则。
如果a->b
和b->c
,那么a->c
。这就是传递性规则。
因此,让我们将其应用于一个简单的示例:
int a;
volatile int b;
thread1(){
a=1;
b=1
}
thread2(){
int rb=b;
int ra=a;
if(rb==1 and ra==0) print("violation");
}
所以问题是,如果thread2看到rb=1,它会看到ra=1吗?
a=1->b=1
由于程序顺序规则。
b=1->rb=b
(因为我们看到了值1)由于易失性变量规则。
rb=b->ra=a
由于程序顺序规则。
现在,我们可以应用传递性规则两次,我们可以得出结论,a=1->ra=a
。因此,ra必须是1。
这意味着:
a=1
和b=1
不能是reordered.rb=b
,ra=a
不能重新排序否则,我们可能会得到一个rb=1
和ra=0
。
https://stackoverflow.com/questions/66623817
复制相似问题