Java是易变的,发生在作用域之前

Java是易变的,发生在作用域之前,java,volatile,Java,Volatile,教程说 无法对其他变量的读取和写入进行重新排序 写入易失性变量后,如果 在写入volatile变量之前发生。读/写 在写入易失性变量之前,确保“发生” 在“写入易失性变量”之前 “写入易失性变量之前”是什么意思?这是否意味着以前的读/写操作与我们写入volatile变量的方法相同?或者它是一个更大的范围(也在调用堆栈更高的方法中)?JVM可以重新排序操作。例如,如果我们有i,j变量和代码 i = 1; j = 2; JVM可以以重新排序的方式运行它 j = 2; i = 1; 但是,如果j变

教程说

无法对其他变量的读取和写入进行重新排序 写入易失性变量后,如果 在写入volatile变量之前发生。读/写 在写入易失性变量之前,确保“发生” 在“写入易失性变量”之前


“写入易失性变量之前”是什么意思?这是否意味着以前的读/写操作与我们写入volatile变量的方法相同?或者它是一个更大的范围(也在调用堆栈更高的方法中)?

JVM可以重新排序操作。例如,如果我们有
i
j
变量和代码

i = 1;
j = 2;
JVM可以以重新排序的方式运行它

j = 2;
i = 1;
但是,如果
j
变量标记为
volatile
,那么JVM运行的操作仅为

i = 1;
j = 2;

写入
i
“发生在写入易失性变量之前”
j

JVM确保在读取易失性变量之前写入该变量。拿两条线。可以保证,对于单个线程,执行遵循一种仿佛串行的语义。基本上,您可以假设在同一线程中的两次执行之间存在隐式的b/w关系(编译器仍然可以自由地对指令重新排序)。基本上,单个线程的总顺序是b/w,它的指令受before关系的控制

一个多线程程序有许多这样的偏序(每个线程在本地指令集中有一个总的顺序,但在线程之间没有全局的顺序),但在全局指令集中没有总的顺序。同步就是给你的程序尽可能多的总顺序

回到易失性变量,当线程从中读取时,JVM确保对它的所有写入都发生在读取之前。现在由于这个顺序,写入线程在写入变量之前所做的一切对于从中读取的线程都是可见的。因此,是的,为了回答您的问题,即使调用堆栈中的变量也应该对读取线程可见

我会试着画一幅直观的图画。这两个线程可以被想象成两条平行的轨道,对易失性变量的写入可以是其中一条与它们并行的轨道。你基本上得到了一个

        A -----
              |
              |
              ------- B

通过两个执行线程形成总顺序b/w。由于这个总顺序,A在轨枕之前的所有内容都应该在轨枕之后对B可见。

JMM是根据发生在之前的关系定义的,我们称之为
->
。如果
a->b
,那么
b
应该可以看到
a
的所有内容。这意味着在重新排序加载/存储时存在约束

如果
a
是易失性写入,而
b
是同一变量的后续易失性读取,则
a->b
。这称为volatile变量规则

如果代码中
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
    不能重新排序
  • rb=b
    ra=a
    无法重新排序

否则,我们最终可能会在附件中的伟大答案中回答出
rb=1
ra=0

。这是否回答了您的问题?不完全是这样,它没有给出关于范围的信息,因为变量x和v是用相同的方法编写的