易失性读取是否发生在易失性写入之前?

时间:2013-05-17 17:54:16

标签: java multithreading synchronization java-memory-model

我试着理解为什么这个例子是一个正确同步的程序:

a - volatile
Thread1:
x=a
Thread2:
a=5

因为存在冲突的访问(存在对a的写入和读取)所以在每个顺序一致性执行中必须发生 - 在该访问之间的关系之前。 假设一个顺序执行:

1. x=a
2. a=5

1发生在2之前,为什么?

4 个答案:

答案 0 :(得分:7)

  

1发生在2之前,为什么?

我并非100%确定我理解你的问题。

如果你有一个易变变量a并且有一个线程正在读取它而另一个正在写入它,那么这些访问的顺序可以是 顺序。这是一个race condition。 JVM和Java内存模型(JMM)保证的内容取决于首先发生的操作。

写入可能刚刚发生,读取会看到更新的值。或者写入可能在读取后发生。因此x可以是5,也可以是a之前的值。

  

每次顺序一致性执行必须在该访问之间的关系之前发生

我不确定这意味着什么,所以我会尝试具体。 "发生在关系之前" with volatile表示在读取相同变量之前所有先前的内存写入volatile变量都保证已完成。但是这种保证决不会解释两个volatile操作之间的时间,这两个操作受到竞争条件的影响。保证读者可以看到写入,但如果写入发生在读取之前,

您可能认为这是一个非常弱的保证,但在线程中,通过使用本地CPU缓存可以显着提高其性能,读取字段的值可能来自缓存的内存段而不是中央内存。保证对于确保在发生volatile读取时本地线程内存无效和更新至关重要,以便线程可以正确共享数据。

同样,JVM和JMM保证,如果您正在从volatile字段a进行读取,那么在读取之前发生的同一字段的任何写入,将被它看到 - 写入的值将被正确发布并且对阅读线程可见。但是,这种保证决不会确定顺序。它并不是说写入必须在读取之前发生。

答案 1 :(得分:4)

不,之前(以同步顺序)易失性读取同一变量的易失性写入不一定发生在易失性写入之前。

这意味着他们可能处于“数据竞争”中,因为它们是“冲突的访问,而不是由先前发生的关系命令”。如果这是真的,几乎所有程序都包含数据竞争:)但它可能是一个规范错误。永远不应将易失性读写视为数据竞争。如果程序中的所有变量都是易失性的,则所有执行都是顺序一致的。见http://cs.oswego.edu/pipermail/concurrency-interest/2012-January/008927.html

答案 2 :(得分:1)

很抱歉,但您无法正确说出JVM如何根据JVM的“内存模型”优化代码。您必须使用Java的高级工具来定义您想要的内容。

所以volatile只意味着没有用于变量的“线程间缓存”。

如果您想要更严格的订单,则必须使用同步块。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

答案 3 :(得分:1)

易失性和发生之前仅在读取字段驱动某些条件时才有用。例如:

volatile int a;
int b =0;
Thread-1:
   b = 5;
   a = 10;
Thread-2
   c = b + a;

在这种情况下没有发生 - 之前,a可以是10或0而b可以是5或0,因此结果c可以是0,5,10或15.如果读取a意味着一些其他条件然后发生在之前 - 例如:

int b = 0;
volatile int a = 0;
Thread-1:
   b = 5
   a = 10;
Thread 2: 
   if(a == 10){
      c = b + a;
   }

在这种情况下,您将确保c = 15,因为a==10的读取意味着b = 5的写入发生在写a = 10之前

编辑:更新添加顺序,如灰色

所指出的不一致