0

我有这个简单的 Jcstress 测试:

package io.denery;


import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.IIII_Result;

@JCStressTest
@Outcome(expect = Expect.ACCEPTABLE_INTERESTING)
@State
public class RaceIRIWTest {
    volatile int x, y;

    @Actor
    public void actor1() {
            x = 1;
    }

    @Actor
    public void actor2() {
            y = 1;
    }

    @Actor
    public void actor3(IIII_Result r) {
            r.r1 = x;
            r.r2 = y;
    }

    @Actor
    public void actor4(IIII_Result r) {
            r.r3 = y;
            r.r4 = x;
    }
}

但是这个测试的结果是:

[OK] io.denery.RaceIRIWTest(JVM 参数:[-XX:+UnlockDiagnosticVMOptions,-XX:+WhiteBoxAPI,-XX:-RestrictContended,-Dfile.encoding=UTF-8,-Duser.country=RU,-Duser。 language=en, -Duser.variant, -XX:-TieredCompilation, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN]) Observed state Occurrences Expectation Interpretation
0, 0, 0, 0 22,180,923 ACCEPTABLE_INTERESTING
0, 0, 0, 1 721,581 ACCEPTABLE_INTERESTING
0, 0, 1, 0 13,347 ACCEPTABLE_INTERESTING
0, 0, 1, 1 456,971 ACCEPTABLE_INTERESTING
0, 1, 0, 0 344,068 ACCEPTABLE_INTERESTING 0, 1, 0
, 1, 36 ACCEPTABLE_INTERESTING
528,641 ACCEPTABLE_INTERESTING
0, 1, 1, 1 258,265 ACCEPTABLE_INTERESTING
1, 0, 0, 0 204,088 ACCEPTABLE_INTERESTING
1, 0, 0, 1 667,580 ACCEPTABLE_INTERESTING
1, 0, 1, 1 94,877 ACCEPTABLE_INTERESTING
1, 1, 0, 0 663,159 ACCEPTABLE_INTERESTING 1 ,
1, 0, 1 306,251 ACCEPTABLE_INTERESTING
, 0 128,608 ACCEPTABLE_INTERESTING
1, 1, 1, 1 18,838,186 ACCEPTABLE_INTERESTING

我们看到了竞态条件,但是如果我放入staticvolatile int x, y删除volatile关键字,那么 jcstress 测试的结果将是:

[OK] io.denery.RaceIRIWTest(JVM 参数:[-XX:+UnlockDiagnosticVMOptions,-XX:+WhiteBoxAPI,-XX:-RestrictContended,-Dfile.encoding=UTF-8,-Duser.country=RU,-Duser。 language=en, -Duser.variant, -XX:-TieredCompilation, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN]) Observed state Occurrences Expectation 解释
1, 1, 1, 1 100,299,061 ACCEPTABLE_INTERESTING

为什么 volatile 不修复竞争条件,但 static 关键字修复它?或者这是一个 Jcstress 问题?

4

1 回答 1

0

我不是 JCStress 专家。

但是您缺少的是您需要指定法律结果,请参阅下面的链接以获取示例。

https://github.com/openjdk/jcstress/blob/master/jcstress-samples/src/main/java/org/openjdk/jcstress/samples/concurrency/mutex/Mutex_02_DekkerAlgorithm.java

【禁案】

在 IRIW 测试的情况下,您希望防止不同 CPU 对不同地址的存储出现乱序。所以你想防止那个演员 3 看到r1=1, r2=0(so see xbefore y)。演员 4 看到r3=1, r4=0了(之前看到yx)。所以禁止的情况是1,0,1,0

[易挥发的]

当我用 volatile 检查结果时,没有遇到禁止的情况。原因是具有 volatile 的示例没有数据竞争,因此它只产生顺序一致 (SC) 执行。使用 SC 总是有一些总顺序来解释执行的加载/存储。因为有总排序,所以也会排序存储到不同CPU下发的不同地址。由于 SC 执行需要与程序顺序 (PO) 一致,因此 SC 将防止负载被重新排序。

[静态不挥发]

带有静态的示例包含数据竞争,因为在读取和写入之间的边缘之前没有发生。所以只有静态允许看到被禁止的执行1,0,1,0

一个简单的解释是,如果允许乱序加载(例如 ARM),JIT 或 CPU 会重新排序 2 个加载。我的猜测是,您只会看到1,1,1,1因为xJCStressy不会取消设置;但我不太了解 JCStress。

[多余]

为了使示例更有趣,我将使 2 个存储不透明存储和负载不透明加载,并在 2 个负载之间放置一个 [LoadLoad] 栅栏,以确保它们按顺序执行。Opaque 将确保加载/存储不会被优化,它将提供原子性(基本保证),但不会提供关于加载/存储到不同地址的任何排序保证。因此,您将测试 CPU 的内存一致性保证。

IRIW 不应该在现代 CPU 上失败,因为它们是多副本原子的,我所知道的唯一可能表现出这种行为的 CPU 是 PowerPC。

于 2021-10-11T06:14:34.623 回答