顺序一致性volatile解释
Sequential consistency volatile explanation
我正在观看来自 java jpoint 会议的视频。
我对 Alexey Shipilev 报告中的以下幻灯片有疑问:
抱歉 non-english 幻灯片。实际上作者说变量集不可能是
r1 = 1 (Y)
r2 = 0 (x)
r3 = 1 (x)
r4 = 0 (Y)
根据视频,他暗示这很明显。
有人可以解释为什么这个值根据 JMM 设置不可能吗?
P.S.
如果我理解正确的 Alexey 符号,它遵循以下代码:
public class SequentialConsistency {
static volatile int x;
static volatile int y;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
x = 1;
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
y = 1;
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("r1=" + x + ", r2=" + y);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("r3=" + x + ", r4=" + y);
}
}).start();
}
}
相信我明白了。
假设我们有 4 个线程。 t1-t4(按图从左到右)
t3 读取 y 然后 x 我们看到结果
y=1
x=0
表示顺序如下:
- t1 写入 y
- t3 读作 y
- t3 读作 x
- t2 写入 x
这是唯一可能的序列。
让我们检查它的 t4 读数:
x=1
y=0
根据 t3 的推理,这意味着
t2 write
发生在 t1 write
之前,但它与 t3 输出相矛盾,因此不可能
您可以为此代码构建详尽的 SC 执行列表,并实现没有 SC 执行收益 (1, 0, 1, 0)。
模型方面,很容易争论。同步顺序 (SO) 一致性表示同步读取应该看到 SO 中的最后同步写入。 SO-PO 一致性表示 SO 应该与程序顺序一致。
这允许通过反证法勾勒出证明。假设产生 (1, 0, 1, 0) 的执行存在。然后,由于 SO 一致性,在那些读取到零的执行中必须按以下顺序:
(r2 = x):0 --so--> (x = 1) [1]
(r4 = y):0 --so--> (y = 1) [2]
...另外两个读取必须按此顺序与写入才能看到它们(由于 SO 一致性):
(x = 1) --so--> (r3 = x):1 [3]
(y = 1) --so--> (r1 = y):1 [4]
...此外,由于 SO-PO 一致性:
(r1 = y):1 --po--> (r2 = x):0 [5]
(r3 = x):1 --po--> (r4 = y):0 [6]
这会产生奇怪的可传递 SO,它是循环的:
(r2 = x):0 --so--> (r3 = x):1 --so--> (r4 = y):0 --so--> (r1 = y):1 --so--> (r2 = x):0
[1,3] [6] [2,4] [5]
请注意,对于上面执行中的任何一对动作 A != B,我们可以说 (A --so--> B)
和 (B --so--> A)
——这称为对称性。根据定义,SO 是全阶,全阶是 antisymmetric,这里我们有对称的。我们已经到了矛盾的地步,因此这种处决是不存在的。 Q.E.D.
您可以用更简单的形式来考虑它(尽管您不能比 Aleksey 所说的更正确)。 java 中的 volatile
提供 sequential consistency
,这意味着操作在全局和原子顺序中完成 ,如 。如果您写入一个字段 (x = 1
),每个人都会看到该写入。为了根据您的示例使其更正确,如果有 ThreadA
执行 x = 1
,ThreadB
(执行 r2 = x
)和 ThreadC
(执行r3 = x
),会将 x
读作 1
。这就是顺序一致性的保证。
幻灯片显示在 SC
(顺序一致)执行中:1, 0, 1, 0
是不可能的。因为那意味着:
ThreadA wrote to x value of 1
ThreadB observed that value to be 1 (via r2 = x)
ThreadC observed that value to be 0 (via r3 = x)
但在 SC 世界中,一旦写入发生(我们知道它发生是因为 ThreadB
已经观察到它),其他所有人都必须观察到它。这里不是这种情况:ThreadC
将 x
视为 0
。因此,这打破了 SC。
你必须记住他的观点是——“我们可以用 release/acquire 替换每个 volatile 吗?(毕竟它更便宜)”。而答案是否定的,因为SC禁止1, 0, 1, 0
,而release/acquire
允许
我正在观看来自 java jpoint 会议的视频。
我对 Alexey Shipilev 报告中的以下幻灯片有疑问:
抱歉 non-english 幻灯片。实际上作者说变量集不可能是
r1 = 1 (Y)
r2 = 0 (x)
r3 = 1 (x)
r4 = 0 (Y)
根据视频,他暗示这很明显。
有人可以解释为什么这个值根据 JMM 设置不可能吗?
P.S.
如果我理解正确的 Alexey 符号,它遵循以下代码:
public class SequentialConsistency {
static volatile int x;
static volatile int y;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
x = 1;
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
y = 1;
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("r1=" + x + ", r2=" + y);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("r3=" + x + ", r4=" + y);
}
}).start();
}
}
相信我明白了。
假设我们有 4 个线程。 t1-t4(按图从左到右)
t3 读取 y 然后 x 我们看到结果
y=1
x=0
表示顺序如下:
- t1 写入 y
- t3 读作 y
- t3 读作 x
- t2 写入 x
这是唯一可能的序列。
让我们检查它的 t4 读数:
x=1
y=0
根据 t3 的推理,这意味着
t2 write
发生在 t1 write
之前,但它与 t3 输出相矛盾,因此不可能
您可以为此代码构建详尽的 SC 执行列表,并实现没有 SC 执行收益 (1, 0, 1, 0)。
模型方面,很容易争论。同步顺序 (SO) 一致性表示同步读取应该看到 SO 中的最后同步写入。 SO-PO 一致性表示 SO 应该与程序顺序一致。
这允许通过反证法勾勒出证明。假设产生 (1, 0, 1, 0) 的执行存在。然后,由于 SO 一致性,在那些读取到零的执行中必须按以下顺序:
(r2 = x):0 --so--> (x = 1) [1]
(r4 = y):0 --so--> (y = 1) [2]
...另外两个读取必须按此顺序与写入才能看到它们(由于 SO 一致性):
(x = 1) --so--> (r3 = x):1 [3]
(y = 1) --so--> (r1 = y):1 [4]
...此外,由于 SO-PO 一致性:
(r1 = y):1 --po--> (r2 = x):0 [5]
(r3 = x):1 --po--> (r4 = y):0 [6]
这会产生奇怪的可传递 SO,它是循环的:
(r2 = x):0 --so--> (r3 = x):1 --so--> (r4 = y):0 --so--> (r1 = y):1 --so--> (r2 = x):0
[1,3] [6] [2,4] [5]
请注意,对于上面执行中的任何一对动作 A != B,我们可以说 (A --so--> B)
和 (B --so--> A)
——这称为对称性。根据定义,SO 是全阶,全阶是 antisymmetric,这里我们有对称的。我们已经到了矛盾的地步,因此这种处决是不存在的。 Q.E.D.
您可以用更简单的形式来考虑它(尽管您不能比 Aleksey 所说的更正确)。 java 中的 volatile
提供 sequential consistency
,这意味着操作在全局和原子顺序中完成 ,如 。如果您写入一个字段 (x = 1
),每个人都会看到该写入。为了根据您的示例使其更正确,如果有 ThreadA
执行 x = 1
,ThreadB
(执行 r2 = x
)和 ThreadC
(执行r3 = x
),会将 x
读作 1
。这就是顺序一致性的保证。
幻灯片显示在 SC
(顺序一致)执行中:1, 0, 1, 0
是不可能的。因为那意味着:
ThreadA wrote to x value of 1
ThreadB observed that value to be 1 (via r2 = x)
ThreadC observed that value to be 0 (via r3 = x)
但在 SC 世界中,一旦写入发生(我们知道它发生是因为 ThreadB
已经观察到它),其他所有人都必须观察到它。这里不是这种情况:ThreadC
将 x
视为 0
。因此,这打破了 SC。
你必须记住他的观点是——“我们可以用 release/acquire 替换每个 volatile 吗?(毕竟它更便宜)”。而答案是否定的,因为SC禁止1, 0, 1, 0
,而release/acquire
允许