当前位置: 首页 > 知识库问答 >
问题:

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

漆雕皓轩
2023-03-14

教程http://tutorials.jenkov.com/java-concurrency/volatile.html说

如果读取/写入最初发生在对易失性变量的写入之前,则不能将从其他变量的读取和写入重新排序为在对易失性变量的写入之后发生。写入挥发性变量之前的读取/写入保证在写入挥发性变量之前“发生”。

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

共有3个答案

冯鸿哲
2023-03-14

JMM是根据发生在之前的关系定义的,我们称之为-

如果a是易失性写入,而b是同一变量的后续易失性读取,则a-

如果a出现在代码中b之前,则a-

如果a-

让我们把这个应用到一个简单的例子:

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-

rb=b-

现在我们可以应用及物性规则两次,我们可以得出结论,a=1-

这意味着:

  • a=1b=1不能重新排序。
  • rb=bra=a不能重新排序

否则我们可能会得到一个rb=1ra=0

魏毅
2023-03-14

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

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

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

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

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

通过两条执行线形成总订单。由于这个总的顺序,A在卧铺之前的一切都应该在卧铺之后对B可见。

贺宏富
2023-03-14

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

i = 1;
j = 2;

JVM可以以重新排序的方式运行它

j = 2;
i = 1;

但是,如果j变量标记为易失性,则JVM仅以以下方式运行操作

i = 1;
j = 2;

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

 类似资料:
  • 问题内容: 我想说明 先发生的 关系如何与 volatile 变量一起使用。让我们有以下变量: 和线程A: 和线程B: 根据Java内存模型(JMM),以下语句正确吗? 如果没有,正确的解释是什么? 总是在 发生之前 __仅在JMM中实际发生时才 发生- 在JMM中 __ 如果实际上发生在时间之前,则发生在JMM中-before -before (并且将可预测地分配) 否则,和之间的顺序不确定,并

  • 问题内容: 我有两个线程: 线程数:1 线程数:2 在这里,a和b被声明为volatile。我不了解如何在a = 1之间创建“先发生”边;y = a; 在x = b之间; 并且b = 1; 我知道通过使用volatile变量可以防止从线程缓存中读取过时的值。但是,可变变量如何才能确保在订购前发生。 具体来说,我不明白这一点: 后续每次读取同一字段之前,都会对易失字段进行写操作。 e头有效吗? 问题

  • 我想澄清发生前关系是如何与不稳定变量一起工作的。让我们有以下变量: 和线程A: 和线程B: 根据Java内存模型(JMM),以下语句是否正确?如果不是,正确的解释是什么? 总是发生-之前 在JMM中发生在之前,只有当它实际发生在时间之前 在JMM中发生在之前(并且将被可预测地分配)如果实际发生在

  • 主要内容:成员变量,局部变量变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。 根据作用域的不同,一般将变量分为不同的类型:成员变量和局部变量。下面对这几种变量进行详细说明。 成员变量 Java 的成员变量有两种,分别是全局变量和静态变量(类变量)。定义在方法体和语句块之外,不属于任何一个方法,作用域是整个类。 名称 修饰 访问 生命周期 全局变量(实例变量)

  • 我需要取回一个在未来的onSuccess块中被改变的变量。这是我的示例代码。 返回为无,不会变为,或。那么这是否意味着onSuccess块中变量的值无法返回?然而,这似乎奏效了: 我可以在未来的onSuccess块中做什么,以便根据未来的结果是整数还是字符串,将< code>ret的值返回为< code>1或< code>2?在这种情况下,scala的作用域规则是什么?(我用的是akka)。感谢任

  • 问题内容: 我有一段代码看起来像这样: 片段A: 根据我的理解,由于的读取不同步,因此如果线程A 在下午1点创建了一个,而线程B 在下午2点进行了读取,则很可能返回0或1(即使线程A在1.05 pm完成了对对象的初始化) )。 所以我添加到: 片段B: 一切都很好,除了我在想,如果我将其修改为 Snippet C ,变量是否仍正确同步? 片段C: 使用 代码片段C ,是否可以保证线程A在下午1:0