我试着理解为什么这个例子是一个正确同步的程序:
a - volatile
Thread1:
x=a
Thread2:
a=5
因为存在冲突的访问(存在对a的写入和读取)所以在每个顺序一致性执行中必须发生 - 在该访问之间的关系之前。 假设一个顺序执行:
1. x=a
2. a=5
1发生在2之前,为什么?
答案 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
之前
编辑:更新添加顺序,如灰色
所指出的不一致