简体   繁体   English

Java 易失性和发生在 scope 之前

[英]Java volatile and happens-before scope

The tutorial http://tutorials.jenkov.com/java-concurrency/volatile.html says教程http://tutorials.jenkov.com/java-concurrency/volatile.html

Reads from and writes to other variables cannot be reordered to occur after a write to a volatile variable, if the reads / writes originally occurred before the write to the volatile variable.如果读取/写入最初发生在对 volatile 变量的写入之前,则对其他变量的读取和写入不能重新排序以在写入 volatile 变量之后发生。 The reads / writes before a write to a volatile variable are guaranteed to "happen before" the write to the volatile variable.写入 volatile 变量之前的读取/写入保证“发生在”写入 volatile 变量之前。

What is meant by "before the write to the volatile variable"? “在写入 volatile 变量之前”是什么意思? Does it mean previous read/writes in the same method where we are writing to the volatile variable?这是否意味着以前以我们正在写入 volatile 变量的相同方法进行读/写? Or is it a larger scope (also in methods higher up the call stack)?还是更大的 scope (也在调用堆栈更高的方法中)?

JVM can reorder operations. JVM 可以重新排序操作。 For example if we have i , j variables and code例如,如果我们有ij变量和代码

i = 1;
j = 2;

JVM can run this in reordered manner JVM 可以以重新排序的方式运行它

j = 2;
i = 1;

But if the j variable marked as volatile then JVM runs operations only as但是如果j变量标记为volatile则 JVM 仅运行操作

i = 1;
j = 2;

write to i "happens before the write to the volatile variable" j .写入i “发生在写入 volatile 变量之前” j

The JVM ensures that writes to a volatile variable happens-before any reads from it. JVM 确保对 volatile 变量的写入发生在对其进行任何读取之前 Take two threads.取两条线。 It's guarateed that for a single thread, the execution follows an as-if-serial semantics.保证对于单个线程,执行遵循as-if-serial语义。 Basically you can assume that there is an implicit happens-before relationship b/w two executions in the same thread (the compiler is still free to reorder instructions).基本上,您可以假设在同一个线程中存在隐式的先发生关系 b/w 两次执行(编译器仍然可以自由地重新排序指令)。 Basically a single thread has a total order b/w its instructions governed by the happens-before relationship trivially.基本上,单个线程有一个总顺序 b/w 它的指令由发生前的关系控制。

A multi-threaded program has many such partial orders (every thread has a total order in the local instruction set but there is no order globally across threads) but not a total order b/w the global instruction set.多线程程序有许多这样的部分顺序(每个线程在本地指令集中都有一个总顺序,但在线程之间没有全局顺序),但没有全局指令集的总顺序。 Synchronisation is all about giving your program as much total order as possible.同步就是为您的程序提供尽可能多的总顺序。

Coming back to volatile variables, when a thread reads from it, the JVM ensures that all writes to it happened before the read.回到 volatile 变量,当线程从其中读取时,JVM 确保所有对它的写入都发生在读取之前。 Now because of this order, everything the writing thread did before it wrote to the variable become visible to the thread reading from it.现在由于这个顺序,写入线程在写入变量之前所做的一切对从变量读取的线程都是可见的。 So yes, to answer your question, even variables up in the call stack should be visible to the reading thread.所以是的,要回答你的问题,即使调用堆栈中的变量也应该对读取线程可见。

I'll try to draw a visual picture.我会试着画一张视觉图。 The two threads can be imagined as two parallel rails, and write to a volatile variable can be one of the sleepers b/w them.这两个线程可以想象成两个平行的轨道,写入一个 volatile 变量可以是它们的睡眠者之一。 You basically get a你基本上得到一个

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

shaped total order b/w the two threads of execution.形成了两个执行线程的总顺序。 Everything in A before the sleeper should be visible to B after the sleeper because of this total order.由于这个总顺序,在睡眠者之前的 A 中的所有内容都应该在睡眠者之后对 B 可见。

The JMM is defined in terms of happens before relation which we'll call -> . JMM 是根据发生在关系之前定义的,我们称之为-> If a->b , then the b should see everything of a .如果a->b ,那么b应该看到 a 的所有a This means that there are constraints on reordering loads/stores.这意味着对重新排序加载/存储有限制。

If a is a volatile write and b is a subsequent volatile read of the same variable, then a->b .如果a是 volatile 写入, b是同一变量的后续 volatile 读取,则a->b This is called the volatile variable rule.这称为易失性变量规则。

If a occurs before b in the code, then a->b .如果代码中a出现在b之前,则a->b This is called the program order rule.这称为节目顺序规则。

If a->b and b->c , then a->c .如果a->bb->c ,那么a->c This is called the transitivity rule.这称为传递性规则。

So lets apply this to a simple example:因此,让我们将其应用于一个简单的示例:

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");
}

So the question is if thread2 sees rb=1,will it see ra=1?所以问题是如果thread2看到rb=1,它会看到ra=1吗?

a=1->b=1 due to program order rule. a=1->b=1由于程序顺序规则。

b=1->rb=b (since we see the value 1) due to the volatile variable rule.由于 volatile 变量规则, b=1->rb=b (因为我们看到值 1)。

rb=b->ra=a due to program order rule. rb=b->ra=a由于程序顺序规则。

Now we can apply the transitivity rule twice and we can conclude that that a=1->ra=a .现在我们可以应用传递性规则两次,我们可以得出结论a=1->ra=a And therefor ra needs to be 1.因此 ra 需要为 1。

This means that:这意味着:

  • a=1 and b=1 can't be reordered. a=1b=1不能重新排序。
  • rb=b and ra=a can't be reordered rb=bra=a不能重新排序

otherwise we could end up with an rb=1 and ra=0 .否则我们可能会得到一个rb=1ra=0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM