java - 为什么非易失性变量在 CPU 共享缓存上更新?
问题描述
flag 变量不是易失性的,所以我希望在 Thread 1 上看到一个无限循环。但我不明白为什么 Thread1 可以看到 Thread2 对 flag 变量的更新。
为什么非易失性变量在 CPU 共享缓存上更新?这里 volatile 和 non volatile 标志变量之间有区别吗?
static boolean flag = true;
public static void main(String[] args) {
new Thread(() -> {
while(flag){
System.out.println("Running Thread1");
}
}).start();
new Thread(() -> {
flag = false;
System.out.println("flag variable is set to False");
}).start();
}
解决方案
零保证这样一个简单的程序将显示可感知的结果。我的意思是,至少不能保证哪个线程会先启动。
但总的来说,可见性效果仅由java 语言规范来保证,该规范精心构建了所谓的“先发生关系”。这是您拥有的唯一保证,并且确切地说:
对 volatile 字段的写入发生在对该字段的每次后续读取之前。
没有 volatile
,安全网就消失了。你可能会说 - “但我无法复制”。答案是:
...在这次运行中
...在这个平台上
...使用此编译器
... 在这个 CPU 上
等等。
您在其中添加 a System.out.println
(内部将有synchronized
一部分)的事实只会使事情恶化;从某种意义上说,它消除了让一个线程永远运行的更多机会。
我花了一段时间,但我想我可以想出一个例子来证明这可以打破。为此,您需要一个合适的工具:专为这类事情而设计
@JCStressTest
@State
@Outcome(id = "0", expect = Expect.ACCEPTABLE)
@Outcome(id = "3", expect = Expect.ACCEPTABLE_INTERESTING, desc = "racy read!!!")
@Outcome(id = "4", expect = Expect.ACCEPTABLE, desc = "reader thread sees everything that writer did")
public class NoVolatile {
private int y = 1;
private int x = 1;
@Actor
public void writerThread() {
y = 2;
x = 2;
}
@Actor
public void readerThread(I_Result result) {
if(x == 2) {
int local = y;
result.r1 = x + local;
}
}
}
您不需要了解代码(尽管这会有所帮助),但总体而言,它构建了两个“参与者”或两个线程来更改两个独立的值:x
和y
。有趣的部分是:
if(x == 2) {
int local = y;
result.r1 = x + local;
}
如果x == 2
,我们输入 if 分支并且result.r1
应该总是4
,对吗?如果result.r1
是3
,这是什么意思?
这将意味着x == 2
肯定(否则根本不会写入,r1
因此result.r1
将为零),这意味着y == 1
.
这意味着ThreadA
(or writerThread
) 已经执行了一个写操作(我们肯定知道x == 2
and 因此y
也应该是2
),但是ThreadB
( readerThread
) 没有观察到y
是2
; 它仍然被y
视为存在1
。
这些是由那个定义的案例@Outcome(....)
,显然我关心的是那个3
。如果我运行它(由你决定如何运行),我会看到ACCEPTABLE_INTERESTING
输出中确实存在这种情况。
如果我进行一次更改:
private volatile int x = 1;
通过添加volatile
,我开始遵循 JLS 规范。特别是该链接的 3 点:
如果 x 和 y 是同一线程的操作,并且 x 在程序顺序中位于 y 之前,则为 hb(x, y)。
对 volatile 字段的写入发生在对该字段的每次后续读取之前。
如果 hb(x, y) 和 hb(y, z),则 hb(x, z)。
这意味着如果我看到x == 2
,我也必须看到那个y == 2
(不像没有 volatile
)。如果我现在运行该示例,3
则不会成为结果的一部分。
这应该证明non-volatile
读取可能是活泼的,因此会丢失,而volatile
一个 - 不能丢失。
推荐阅读
- python - 如何将值添加到字典中的特定列表?
- flutter - 如何将 TabBarView 与 BLoC 一起使用?
- python - 硒按钮单击不触发内联javascript事件处理程序
- azure-active-directory - 使用 react-adal 为 aad authentiactionn 添加错误页面
- php - 在 MySQLi 查询中使用三元运算符
- javascript - 在 Avatar 组件下方居中社交媒体图标
- google-analytics - 当我仅使用“sourceMedium”或“sourceMedium”+“campaign”维度时,为什么用户数和会话数不同?
- javascript - 是否有推荐/有效的方法来实现具有反应生产的单个文件包?
- node.js - 如何更新 mongo db 中特定数组元素的值
- ios - 显式卸载后无法重新安装 iMessage 扩展程序