问题描述
据我所知,JMM 不遵循顺序一致性,我们需要 volatile 关键字来保证可见性。 没有SC的典型例子经常描述为
A = 0;
B = 0;
--thread 1--
A = 1;
B = 1;
--thread 2--
if(B == 1){
//at here,A is not guarantee equal to 1,A == 0 is also possible
}
这很容易理解。 但是当我想在真正的 Java 代码中重现这个简单的例子时,我失败了。
下面是我希望重现上述概念示例的代码,这里我没有使用 volatile,但是这段代码仍然没有达到 a==0 和 b==1 的情况。
import java.util.LinkedList;
public class test2 {
static int a = 0;
static int b = 0;
static class Multithreading1 extends Thread {
public void run() {
try {
sleep(50);
} catch (InterruptedException e) {
e.printstacktrace();
}
a = 1;
b = 1;
}
}
static class Multithreading2 extends Thread {
public void run() {
while (b == 0) {
Thread.onSpinWait();
}
if (a == 0) {
//never reach
System.out.print(a);
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
a = 0;
b = 0;
Multithreading1 t1 = new Multithreading1();
t1.start();
LinkedList<Multithreading2> t2 = new LinkedList();
int thread_number = 16;
for (int j = 0; j < thread_number; j++) {
t2.add(new Multithreading2());
}
for (int j = 0; j < thread_number; j++) {
t2.get(j).start();
}
for (int j = 0; j < thread_number; j++) {
t2.get(j).join();
}
t1.join();
}
}
}
我做错了什么吗? 或者我的失败背后有什么隐藏的机制,如果是这样,是否可以构建一个真实的代码示例来表明Java不遵循顺序一致性?
顺便说一下,我的java版本是OpenJDK Runtime Environment Corretto-11.0.8.10.1
解决方法
JMM 是一种弱内存模型,这意味着普通加载和存储以及同步操作(例如写入/读取易失性变量或解锁/锁定锁定)之间存在分离。 JMM 遵循 SC-DRF 模型(无数据竞争程序的顺序一致性)。 SC-DRF 意味着如果程序的所有可能执行都没有数据竞争,则程序的行为就好像它是顺序一致的。
请记住,如果您在 X86 上运行,那么由于 X86 内存模型 (TSO),将不会对 2 个存储进行重新排序。它们可以乱序执行,但它们将始终退出并按程序顺序 (PO) 提交到缓存。这同样适用于 2 个负载;加载也将在 PO 中执行,即使在引擎盖下它们可能会被推测性地乱序执行。但这是不可能检测到的,因为如果推测失败,CPU 将刷新管道并重试。
此外,编译器也可以优化代码,从而不会因为(有意的)数据竞争而发生重新排序。
这是 JCStress 可以提供帮助的地方: https://openjdk.java.net/projects/code-tools/jcstress。
,作为 pveentjer already answered,JCStress 可以提供帮助。
我只想补充一点,JCStress 源代码中的示例之一就是您的情况:BasicJMM_06_Causality.PlainReads
。
所以只要研究它的工作原理,您就会发现当我们尝试重现 JMM 的一些极端情况时应该解决哪些细微差别。