【问题标题】:JCStress - strange volatile and static keyword behaviorJCStress - 奇怪的 volatile 和 static 关键字行为
【发布时间】:2021-10-10 17:22:39
【问题描述】:

我有这个简单的 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]) 观察到的状态发生期望解释
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
0, 1, 1, 0 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
1, 1, 1, 0 128,608 ACCEPTABLE_INTERESTING
1、1、1、1 18,838,186 ACCEPTABLE_INTERESTING

我们看到了竞态条件,但是如果我将 static 放入 volatile 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]) 观察到的状态发生期望解释
1、1、1、1 100,299,061 ACCEPTABLE_INTERESTING

为什么 volatile 不能修复竞争条件,而 static 关键字可以修复它?还是 Jcstress 问题?

【问题讨论】:

    标签: java concurrency static volatile jcstress


    【解决方案1】:

    我不是 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(所以在y 之前看到x)。演员 4 看到r3=1, r4=0(所以在x 之前看到y)。所以禁止的情况是1,0,1,0

    [易失性]

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

    [静态无易失]

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

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

    [多余的]

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

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

    【讨论】:

    • 感谢您的回答,我尝试了您的所有情况,包括 [多余] 部分(使用 VarHandles)。所以有很多结果(0、0、0、0;0、0、0、1...等)不包括 1、0、1、0,因为我的计算机上有 2 个 CPU 和 4 个线程在它们之间切换导致这些结果?
    猜你喜欢
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 2018-12-16
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多