【发布时间】:2021-12-02 21:09:34
【问题描述】:
根据此重新排序规则
如果我有这样的代码
volatile int a = 0;
boolean b = false;
foo1(){ a= 10; b = true;}
foo2(){if(b) {assert a==10;}}
让线程 A 运行 foo1,让线程 b 运行 foo2,因为 a= 10 是一个易失性存储,并且 b = true 是一个正常的存储,那么这两个语句可能会被重新排序,这意味着在线程 B 中可能有 b = true 而 a!= 10?对吗?
添加:
感谢您的回答!
我刚开始学习java多线程,一直被关键字volatile困扰。
许多教程都在讨论 volatile 字段的可见性,就像“在对它的写操作完成后,所有读者(特别是其他线程)都可以看到 volatile 字段”。我怀疑已完成的字段写入如何对其他线程(或 CPUS)不可见?
据我了解,完成写入意味着您已成功将文件写回缓存,并且根据 MESI,如果该文件已被所有其他线程缓存,则所有其他线程都应具有无效的缓存行。一个例外(由于我对硬核不是很熟悉,这只是一个猜想)可能结果会被写回寄存器而不是缓存,我不知道在这种情况下是否有一些协议可以保持一致性或 volatile 使其不写入在 java 中注册。
在某些看起来像“隐形”的情况下会发生示例:
A=0,B=0;
thread1{A=1; B=2;}
thread2{if(B==2) {A may be 0 here}}
假设编译器没有对其重新排序,我们在thread2中看到的原因是由于存储缓冲区,我不认为存储缓冲区中的写操作意味着完成写入。由于存储缓冲区和无效队列策略,这使得对变量 A 的写入看起来不可见,但实际上在 thread2 读取 A 时写入操作尚未完成。即使我们使字段 B 易失性,而我们将字段 B 的写入操作设置为带有内存屏障的存储缓冲区,线程 2 可以读取 0 的 b 值并完成。对我来说, volatile 看起来与其声明的文件的可见性无关,而更像是一个边缘,以确保所有写入发生在 ThreadA 中的 volatile 字段写入之前对 volatile 字段读取之后的所有操作都是可见的( volatile read在 ThreadA 中的 volatile 字段写入完成后发生)在另一个 ThreadB 中。
顺便说一句,由于我不是母语人士,我看到可能有我母语的教程(还有一些英文教程)说 volatile 会指示 JVM 线程从主内存中读取 volatile 变量的值,而不是在本地缓存它,我认为这不是真的。我说的对吗?
无论如何,谢谢你的回答,因为不是母语人士,我希望我已经表达清楚了。
【问题讨论】:
-
旁注:您需要为您的进程启用断言,否则
foo2()将只包含一个空的 if 块(即断言永远不会被执行)。 -
@Thomas 好的,谢谢。
标签: java multithreading volatile cpu-cache memory-barriers