简体   繁体   English

Java内存模型:volatile变量和之前发生的

[英]Java memory model: volatile variables and happens-before

I'd like to clarify how happens-before relation works with volatile variables. 我想澄清一下,在关系与volatile变量一起工作之前会发生什么 Let we have the following variables: 我们有以下变量:

public static int i, iDst, vDst;
public static volatile int v;

and thread A: 和线程A:

i = 1;
v = 2;

and thread B: 和线程B:

vDst = v;
iDst = i;

Are the following statements correct in accordance with Java memory model (JMM)? 以下语句是否符合Java内存模型(JMM)? If not, what would be correct interpretation? 如果没有,那么正确的解释是什么?

  • i = 1 always happens-before v = 2 i = 1总是 v = 2 之前发生
  • v = 2 happens-before vDst = v in JMM only if it's actually happens before in time v = 2 发生在JMM中的vDst = v 之前 ,只有它实际发生在时间之前
  • i = 1 happens-before iDst = i in JMM (and iDst will be predictably assigned 1 ) if v = 2 actually happens before vDst = v in time i = 1 发生 -在JMM中iDst = i 之前 (并且iDst将被预测为1 )如果v = 2实际发生在vDst = v之前
  • Otherwise order between i = 1 and iDst = i is undefined and resulting value of iDst is undefined as well 否则顺序之间i = 1iDst = i是未定义和的结果值iDst未定义以及

Mistake in the logic: 错误的逻辑:

There is no "wall clock time" concept in JMM, and we should rely on synchronization order as an ordering guide for v = 2 and vDst = v . JMM中没有“挂钟时间”概念,我们应该依赖同步顺序作为v = 2vDst = v的排序指南。 See the chosen answer for further details. 有关详细信息,请参阅所选答案。

  • i = 1 always happens-before v = 2 i = 1总是 v = 2 之前发生

True. 真正。 By JLS section 17.4.5 , 按JLS第17.4.5节

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y) . 如果xy是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y)


  • v = 2 happens-before vDst = v in JMM only if it's actually happens before in time v = 2 发生在JMM中的vDst = v 之前 ,只有它实际发生在时间之前
  • i = 1 happens-before iDst = i in JMM (and iDst will be predictably assigned 1 ) if v = 2 actually happens before vDst = v in time i = 1 发生 -在JMM中iDst = i 之前 (并且iDst将被预测为1 )如果v = 2实际发生在vDst = v之前

False. 假。 The happens-before order does not make guarantees about things happening before each other in physical time. 事先发生的命令并不能保证物理时间内彼此之前发生的事情。 From the same section of the JLS, 从JLS的同一部分,

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. 应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。 If the reordering produces results consistent with a legal execution, it is not illegal. 如果重新排序产生的结果与合法执行一致,则不是非法的。

It is, however, guaranteed that v = 2 happens-before vDst = v and i = 1 happens-before iDst = i if v = 2 comes before vDst = v in the synchronization order, a total order over the synchronization actions of an execution that is often mistaken for the real-time order. 但是,保证v = 2 发生 - 在 vDst = v 之前发生并且i = 1 发生 - 在 iDst = i 之前如果v = 2在同步顺序中在vDst = v之前vDst = v ,则执行同步动作的总顺序这通常被误认为是实时订单。


  • Otherwise order between i = 1 and iDst = i is undefined and resulting value of iDst is undefined as well 否则顺序之间i = 1iDst = i是未定义和的结果值iDst未定义以及

This is the case if vDst = v comes before v = 2 in the synchronization order, but actual time doesn't come into it. 如果vDst = v在同步顺序中位于v = 2之前,则会vDst = v这种情况,但实际时间不会进入。

Yes all of them are correct according to this section about happens-before order: 是的,所有这些都是正确的 根据本节关于发生 - 在订单之前:

  1. i = 1 always happens-before v = 2 since: i = 1总是 v = 2 之前发生,因为:

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y) . 如果x和y是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y)

  1. v = 2 happens-before vDst = v in JMM only if it's actually happens before in time, since v is volatile, and v = 2 发生在JMM中的vDst = v 之前 ,只有当它实际发生在时间之前,因为v是易失性的,并且

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field. 写入易失性字段(第8.3.1.4节) - 在每次后续读取该字段之前发生。

  1. i = 1 happens-before iDst = i in JMM (and iDst will be predictably assigned 1) if v = 2 actually happens before vDst = v in time. 如果v = 2实际发生在vDst = v之前,则i = 1 发生在JMM中iDst = i 之前 (并且iDst将被预测地指定为1)。 This is because in this case: 这是因为在这种情况下:
    • i = 1 happens-before v = 2 i = 1 发生在 v = 2 之前
    • v = 2 happens-before vDst = v v = 2 发生在 vDst = v 之前
    • vDst = v happens-before iDst = i vDst = v 发生在 iDst = i 之前

If hb(x, y) and hb(y, z) , then hb(x, z) . 如果是hb(x,y)hb(y,z) ,那么hb(x,z)

EDIT: 编辑:

As argued by @user2357112, it seems statements 2 and 3 are not accurately correct. 正如@ user2357112所论证的那样,似乎语句2和3并不准确。 The happens-before relationship does not necessarily impose a timing order between actions having this relationship, as mentioned in the same section of the JLS: 发生在之前的关系并不一定在具有这种关系的动作之间强加一个时间顺序,如JLS的同一部分所述:

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. 应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。 If the reordering produces results consistent with a legal execution, it is not illegal. 如果重新排序产生的结果与合法执行一致,则不是非法的。

Therefore, in terms of the rules mentioned in the JLS, we shouldn't make assumptions on the actual timing of the execution of the statements. 因此,就JLS中提到的规则而言,我们不应该对执行报表的实际时间做出假设。

All synchronization actions (volatile w/r, lock/unlock etc) form a total order. 所有同步操作(易失性w / r,锁定/解锁等)形成总订单。 [1] That is a very strong statement; [1]这是一个非常强烈的声明; it makes analysis easier. 它使分析更容易。 For your volatile v , either read is before write, or write is before read, in this total order. 对于易失性v ,要么在写入之前读取,要么在读取之前读取,在此总顺序中。 The order depends on the actual execution of course. 订单取决于当然的实际执行情况。

From that total order, we can establish partial orders happens-before . 从总订单中,我们可以建立之前发生的部分订单。 [2] If all reads and writes on a variable (volatile or not) are on a partial order chain, it's easy to analyze - a read sees the immediate preceding write. [2]如果对变量(易失性或非易失性)的所有读写都在部分订单链上,则很容易分析 - 读取会看到前一次写入。 That is the main point of JMM - establishing orders on read/writes so they can be reasoned like a sequential execution. 这是JMM的要点 - 在读/写上建立订单,因此它们可以像顺序执行一样被推理。

But what if the volatile read is before the volatile write? 但是如果易失性读取在易失性写入之前怎么办? We need another crucial constraint here - the read must not see the write. 我们需要另一个关键约束 - 读取不能看到写入。 [3] [3]

Therefore, we can reason that, 因此,我们可以推断,

  1. read of v sees either 0 (init value) or 2 (volatile write) 读取v看到0(初始值)或2(易失写入)
  2. if it sees 2, it must be the case that the read is after the write; 如果它看到2,则必须是在写入之后读取的情况; and in that case, we have happens-before chain. 在那种情况下,我们happens-beforehappens-before

Last point - read of i must see one of the writes to i ; 最后一点-读i要看到写入的一个i ; in this example, 0 or 1. It will never see a magic value not from any writes. 在这个例子中,0或1.它永远不会看到任何写入的魔术值。


quoting java8 spec: 引用java8规范:

[1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.4 [1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.4

[2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5 [2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5

[3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.7 [3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.7


random thoughts on the total order: 关于总订单的随机想法:

Because of this total order, we could say one sync action happens before another as if in time . 由于这个总订单,我们可以说一个同步动作发生在另一个之前,就像在时间上一样 That time may not correspond to wall clock, but it's not a bad mental model for our understanding. 那个时间可能与挂钟不对应,但对我们的理解来说,这并不是一个糟糕的心理模型。 (In reality, one action in java corresponds to a storm of hardware activities, it is impossible to define a point in time for it) (实际上,java中的一个动作对应于硬件活动的风暴,不可能为它定义一个时间点)

And even physical time is not absolute. 甚至物理时间也不是绝对的。 Remember that light travels 30cm in 1ns; 请记住,光线在1ns内传播30厘米; on today's CPUs, temporal order is definitely relative. 在今天的CPU上,时间顺序绝对是相对的。 The total order actually requires that there is causality from one action to the next. 总订单实际上要求从一个动作到下一个动作存在因果关系。 That is a very strong requirement, and you bet that JVM tries hard to optimize it. 这是一个非常强烈的要求,你敢打赌JVM会努力优化它。

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

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