我试图理解为什么这个例子是一个正确同步的程序:
a - volatile
Thread1:
x=a
Thread2:
a=5
因为存在冲突的访问(a 的写入和读取),所以在每个顺序一致性执行中,访问之间的关系必须先发生。假设顺序执行之一:
1. x=a
2. a=5
1 发生在 2 之前,为什么?
我试图理解为什么这个例子是一个正确同步的程序:
a - volatile
Thread1:
x=a
Thread2:
a=5
因为存在冲突的访问(a 的写入和读取),所以在每个顺序一致性执行中,访问之间的关系必须先发生。假设顺序执行之一:
1. x=a
2. a=5
1 发生在 2 之前,为什么?
1 发生在 2 之前,为什么?
我不是 100% 确定我理解你的问题。
如果您有一个 volatile 变量a
并且一个线程正在读取它而另一个线程正在写入它,那么这些访问的顺序可以是任意顺序。这是一个竞争条件。JVM 和 Java 内存模型 (JMM) 所保证的内容取决于首先发生的操作。
写入可能刚刚发生,读取会看到更新的值。或者写入可能发生在读取之后。所以x
可以是 的任何一个5
或以前的值a
。
每个顺序一致性执行必须在访问之间发生之前的关系
我不确定这意味着什么,所以我会尽量具体。“发生在关系之前”volatile
意味着volatile
在读取相同变量之前所有先前的内存写入变量都保证已经完成。但这种保证绝不解释volatile
受竞争条件影响的两个操作之间的时间安排。读者可以保证看到写入,但前提是写入发生在读取之前。
您可能认为这是一个非常薄弱的保证,但在线程中,通过使用本地 CPU 缓存可以显着提高性能,读取字段的值可能来自缓存的内存段而不是中央内存。该保证对于确保本地线程内存在volatile
读取发生时无效并更新以便线程可以适当地共享数据至关重要。
同样,JVM 和 JMM 保证,如果您正在读取一个volatile
字段,那么在读取之前发生a
的对同一字段的任何写入都将被它看到——写入的值将被正确发布并且对读取可见线。但是,此保证绝不决定订购。它并不是说写入必须在读取之前发生。
不,在(按同步顺序)对同一变量的易失性写入之前的易失性读取不一定发生在易失性写入之前。
这意味着它们可能处于“数据竞争”中,因为它们是“冲突的访问不是按发生前的关系排序的”。如果这是真的,几乎所有程序都包含数据竞争:) 但这可能是一个规范错误。永远不应将易失性读写视为数据竞争。如果程序中的所有变量都是易失的,那么所有的执行都是顺序一致的。见http://cs.oswego.edu/pipermail/concurrency-interest/2012-January/008927.html
抱歉,您无法正确说明 JVM 将如何根据 JVM 的“内存模型”优化代码。您必须使用 Java 的高级工具来定义您想要的内容。
所以 volatile 仅意味着不会有用于变量的“线程间缓存”。
如果您想要更严格的顺序,则必须使用同步块。
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html
Volatile 和happens-before 仅在读取字段驱动某些条件时才有用。例如:
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
编辑:更新添加顺序,如 Gray 所指出的不一致