如何理解 JSR-133 Happens-Before 太弱 Figure6/7
How to understand JSR-133 Happens-Before is too Weak Figure6/7
在 JSR-133 中,有两个示例说明程序大概已正确同步。
第一个示例由图 6 给出,其中:
x == y == 0
然后,线程 1 通过以下方式修改状态:
r1 = x;
if (r1 != 0)
y = 1;
并且线程 2 通过以下方式修改状态:
r2 = y;
if (r2 != 0)
x = 1;
作者声明此程序已正确同步,但是执行此程序导致r1 == r2 == 1
。后一个值怎么可能?
此外,图7表示初始状态:
x == y == 0
线程 1 修改:
r1 = x;
y = r1;
并且线程 2 正在执行:
r2 = y;
x = r2;
这怎么会导致 r1 == r2 == 42
?
图6
happens-before 模型仅适用于 synchronizes-with 和 happens-before 边。 JLS 给出了一个 happens-before 一致执行的例子:
r1 = x; // sees write of x = 1
y = 1;
r2 = y; // sees write of y = 1
x = 1;
在此示例中,值“凭空出现”:happens-before 一致但打破了因果关系要求(没有顺序一致的执行可以产生这个结果)。因此,这样的处决不是合法的处决。
图 7
r1 === r2 == 42
不是 一个有效的结果。
没有同步,但唯一有效的结果是所有值都为 0,因为没有其他值分配给变量。例如,在 y = r1
中,r1
具有其默认值 (0) 或具有 x
的值 (0) - 在这两种情况下 y
都将为 0。
作为旁注,您可能应该阅读 Chapter 17 of the JLS 而不是原始的 JSR。
图 6 是 JMM 的 out of thing air 限制示例。因为没有字段被标记为 volatile
,所以通常允许 JVM 对示例代码应用优化。这可能会导致意想不到的结果。
假设 线程 1 观察到 x = 1
为真。暂时不要担心,怎么会这样。在这种情况下,线程将设置 y = 1
。后一种观察将证明 x = 1
的初步观察是循环推理。如果这是真的,x = y = 1
将是一个合法的结果,并且 JVM 可以简单地替换两个线程中的代码来写入每个值。 JMM 禁止这种形式的推理,因此 x = y = 0
是根据 JMM 唯一合法的结果。
图 7 中也有类似的论点。同样,假设 线程 1 观察到 x = 42
。在这种情况下,线程将写入 y = 42
。基于这一观察,现在可以争辩说之前的 x = 42
是一个合法的观察。然而,最初的假设是 凭空 禁止的,因此 x = y = 0
是唯一合法的结果。
happens-before 关系的定义仅适用于明确同步的程序,不会禁止假设的循环推理。所以就需要凭空限制。这当然是比较学术的,但是JMM是一个学术模型,定义这个限制还是有必要的。
因此,在任何图中都没有可以取值 x
或 y
的比赛条件,即没有比赛条件, x = y = 0
是唯一可能的观察。由此可见,尽管 volatile
.
缺失,但两个示例都已正确同步
图7:(图6已经被其他答案解释清楚了。)
您可以在论文"The Java Memory Model @ POPL'05"中找到图7中示例代码的解释r1 == r2 == 42
。以下内容来自第 2.2 节:
图 2(在论文中)包含一个需要仔细语义的程序的常见示例。同步不正确;该程序的所有顺序一致执行都表现出线程 1
和 2
之间的数据竞争。但是,我们需要为这段代码提供强有力的保证:我们需要
确保42
这样的值不会凭空出现在r1
和r2
.
中
目前没有任何优化可以允许这样的凭空结果。然而,在未来的攻击性系统中,Thread 1
可以推测性地将值 42
写入 y
,这将允许 Thread 2
读取 42
for y
并将其写出到 x
,这将允许 Thread 1
读取 42
用于 x
,并证明其原始推测写入 42
用于 [=18] =].
像这样的自我辩护的写入推测可能会造成严重的安全违规,需要被禁止。例如,如果凭空产生的值是对线程不应该拥有的对象的引用(例如,由于安全保证),这将是一个严重的问题。
在 JSR-133 中,有两个示例说明程序大概已正确同步。
第一个示例由图 6 给出,其中:
x == y == 0
然后,线程 1 通过以下方式修改状态:
r1 = x;
if (r1 != 0)
y = 1;
并且线程 2 通过以下方式修改状态:
r2 = y;
if (r2 != 0)
x = 1;
作者声明此程序已正确同步,但是执行此程序导致r1 == r2 == 1
。后一个值怎么可能?
此外,图7表示初始状态:
x == y == 0
线程 1 修改:
r1 = x;
y = r1;
并且线程 2 正在执行:
r2 = y;
x = r2;
这怎么会导致 r1 == r2 == 42
?
图6
happens-before 模型仅适用于 synchronizes-with 和 happens-before 边。 JLS 给出了一个 happens-before 一致执行的例子:
r1 = x; // sees write of x = 1
y = 1;
r2 = y; // sees write of y = 1
x = 1;
在此示例中,值“凭空出现”:happens-before 一致但打破了因果关系要求(没有顺序一致的执行可以产生这个结果)。因此,这样的处决不是合法的处决。
图 7
r1 === r2 == 42
不是 一个有效的结果。
没有同步,但唯一有效的结果是所有值都为 0,因为没有其他值分配给变量。例如,在 y = r1
中,r1
具有其默认值 (0) 或具有 x
的值 (0) - 在这两种情况下 y
都将为 0。
作为旁注,您可能应该阅读 Chapter 17 of the JLS 而不是原始的 JSR。
图 6 是 JMM 的 out of thing air 限制示例。因为没有字段被标记为 volatile
,所以通常允许 JVM 对示例代码应用优化。这可能会导致意想不到的结果。
假设 线程 1 观察到 x = 1
为真。暂时不要担心,怎么会这样。在这种情况下,线程将设置 y = 1
。后一种观察将证明 x = 1
的初步观察是循环推理。如果这是真的,x = y = 1
将是一个合法的结果,并且 JVM 可以简单地替换两个线程中的代码来写入每个值。 JMM 禁止这种形式的推理,因此 x = y = 0
是根据 JMM 唯一合法的结果。
图 7 中也有类似的论点。同样,假设 线程 1 观察到 x = 42
。在这种情况下,线程将写入 y = 42
。基于这一观察,现在可以争辩说之前的 x = 42
是一个合法的观察。然而,最初的假设是 凭空 禁止的,因此 x = y = 0
是唯一合法的结果。
happens-before 关系的定义仅适用于明确同步的程序,不会禁止假设的循环推理。所以就需要凭空限制。这当然是比较学术的,但是JMM是一个学术模型,定义这个限制还是有必要的。
因此,在任何图中都没有可以取值 x
或 y
的比赛条件,即没有比赛条件, x = y = 0
是唯一可能的观察。由此可见,尽管 volatile
.
图7:(图6已经被其他答案解释清楚了。)
您可以在论文"The Java Memory Model @ POPL'05"中找到图7中示例代码的解释r1 == r2 == 42
。以下内容来自第 2.2 节:
图 2(在论文中)包含一个需要仔细语义的程序的常见示例。同步不正确;该程序的所有顺序一致执行都表现出线程 1
和 2
之间的数据竞争。但是,我们需要为这段代码提供强有力的保证:我们需要
确保42
这样的值不会凭空出现在r1
和r2
.
目前没有任何优化可以允许这样的凭空结果。然而,在未来的攻击性系统中,Thread 1
可以推测性地将值 42
写入 y
,这将允许 Thread 2
读取 42
for y
并将其写出到 x
,这将允许 Thread 1
读取 42
用于 x
,并证明其原始推测写入 42
用于 [=18] =].
像这样的自我辩护的写入推测可能会造成严重的安全违规,需要被禁止。例如,如果凭空产生的值是对线程不应该拥有的对象的引用(例如,由于安全保证),这将是一个严重的问题。