Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/387.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中的多线程状态可见性:有没有办法将JVM变成最坏的情况?_Java_Concurrency_Jvm_Race Condition_Jvm Arguments - Fatal编程技术网

Java中的多线程状态可见性:有没有办法将JVM变成最坏的情况?

Java中的多线程状态可见性:有没有办法将JVM变成最坏的情况?,java,concurrency,jvm,race-condition,jvm-arguments,Java,Concurrency,Jvm,Race Condition,Jvm Arguments,假设我们的代码有两个线程(A和B),它们在某个地方引用了这个类的同一个实例: public class MyValueHolder { private int value = 1; // ... getter and setter } 当线程A执行myValueHolder.setValue(7)时,不能保证线程B会读取该值:myValueHolder.getValue()理论上可以永远返回1 但实际上,硬件迟早会清除二级缓存,因此线程B迟早会读取7(通常更早) 有没有办

假设我们的代码有两个线程(A和B),它们在某个地方引用了这个类的同一个实例:

public class MyValueHolder {

    private int value = 1;

    // ... getter and setter

}
当线程A执行
myValueHolder.setValue(7)
时,不能保证线程B会读取该值:
myValueHolder.getValue()
理论上可以永远返回
1

但实际上,硬件迟早会清除二级缓存,因此线程B迟早会读取
7
(通常更早)


有没有办法让JVM模拟最坏的情况,在这种情况下,它会为线程B永远返回
1
在这种情况下,使用我们现有的测试测试我们的多线程代码非常有用。

不是在真正的机器上,不幸的是,测试多线程代码仍然很困难

正如您所说,硬件将清除二级缓存,而JVM对此没有控制权。JSL只指定必须发生的事情,在这种情况下,
B
可能永远看不到
value
的更新值

在真正的机器上强制这种情况发生的唯一方法是改变代码,使您的测试策略无效,即您最终测试不同的代码


但是,您可能能够在模拟不清除二级缓存的硬件的模拟器上运行此功能。不过听起来需要很多努力

这里有一个简单的方法:只需注释掉
setValue
的代码即可。您可以在测试后取消注释它。由于在许多类似的情况下,需要一种机制来伪装失败,因此最好为所有此类情况建立一种通用机制

我认为您指的是一种称为“虚假共享”的原则,即不同的CPU必须同步其缓存,否则,您所描述的数据可能会变得不匹配。英特尔在其文章中介绍了一些诊断此问题的有用工具。这是一个相关的报价:

避免错误共享的主要方法是通过代码 检查。线程动态或全局访问的实例 分配的共享数据结构是错误的潜在来源 分享。请注意,虚假共享可以通过以下事实来掩盖: 线程可能正在访问完全不同的全局变量 恰好在记忆中是相对紧密的。线程本地存储 或者可以排除局部变量作为虚假共享的来源

尽管本文中描述的方法不是您所要求的(从JVM强制执行最坏情况的行为),但正如前面所述,这实际上是不可能的。本文描述的方法是我所知道的尝试诊断和避免错误共享的最佳方法


网络上还有其他资源可以解决这个问题。我没有尝试过这种方法,所以我不能担保它,但我认为作者的想法是正确的。你可以考虑尝试他的建议。

< P> >有一个java编译器,它会有意地执行尽可能多的奇怪(但允许的)转换,以便能够更容易地破坏线程不安全代码,就像C。不幸的是,这样的编译器不存在(据我所知)。 同时,您可以尝试使用库*并在几种体系结构上运行代码,如果可能的话,可以使用(即不是x86)尝试中断代码:

Java并发压力测试(jcstress)是一个实验性工具,一套测试有助于研究JVM、类库和硬件中并发支持的正确性

但最后,不幸的是,证明一段代码100%正确的唯一方法是代码检查(我不知道有哪种静态代码分析工具能够检测所有竞争条件)

*我没有使用过它,我不清楚哪个jcstress和哪个是最新的(我怀疑jcstress)。

这里的维护者。回答这个问题有多种方法

  • 最简单的解决方案是将getter包装在循环中,并让JIT提升它。这允许非易失性字段读取,并通过编译器优化模拟可见性故障
  • 更复杂的技巧包括获得OpenJDK的调试版本,并使用
    -XX:+StressLCM-XX:+StressGCM
    ,有效地执行指令调度模糊化。有可能问题的负载会浮动到某个地方,您可以通过产品的常规测试进行检测
  • 我不确定是否有实用的硬件将写入的值保存足够长的时间以缓存一致性,但是使用jcstress构建测试用例有点容易。您必须记住(1)中的优化也可能发生,因此我们需要采用一种技巧来防止这种情况。我想应该有办法

  • 您给出的示例在中被描述为不正确同步。我认为这总是不正确的,迟早会导致错误。大多数时间以后:-)

    要查找这些不正确同步的代码块,我使用以下算法:

    使用仪器记录所有现场修改的螺纹。如果一个字段被多个线程修改而没有同步,那么我发现了一个数据竞争


    我在内部实现了这个算法,这是一个在java程序中查找数据竞争的工具。

    我以前在内存模型列表中提出了一个用于测试的最坏情况的JVM,但这个想法似乎并不流行

    因此,如何利用现有技术获得“最坏情况下的JVM行为”,即如何测试问题中的场景并使其每次都失败。您可以尝试使用最差的内存模型来查找设置,但这不太可能是完美的

    我经常考虑的是使用分布式JVM,类似于我认为Terracotta在幕后的工作方式