3

JCStress用来测试最终变量。我知道这final可以用来确保当您构造一个对象时,访问该对象的另一个线程不会看到该对象处于部分构造状态。现在我有一个A.java

public class A {
    final int f;

    A() {
        this.f = 42;
    }

}

据我所知,构造函数应该这样执行

A temp = <new>
temp.f = 42
<freeze value>
fv = temp

现在我正在使用下面提到的测试。

@JCStressTest
@State
public class FinalField {
    A a;

    @Actor
    public void writer() {
        a = new A();
    }

    @Actor
    public void reader(I_Result result) {
        A ta = a;
        if (ta != null) {
            result.r1 = ta.f; 
        }
    }

}

现在,为什么我在输出中看到值 0?我的 CPU 架构是 x86,因此使用负载重新排序存储也没有意义。我得到的输出是

           0    94,922,153     FORBIDDEN  No default case provided, assume FORBIDDEN                  
          42    48,638,587     ACCEPTABLE  Final value initialized    

另外,我发现另一件不寻常的事情是,当我将字段声明astatic. 我的输出只有 42,这是为什么呢?

          42   299,477,390     ACCEPTABLE  Final value initialized      
4

3 回答 3

1

另一个答案已经解释了为什么你遇到问题0and static
但即使解决了这些问题,也很难重现部分初始化。
因此,我建议您查看 JCStress 源代码:它包含示例,其中一个(JMMSample_06_Finals)已经完成了您想要的操作。

于 2021-05-16T17:54:50.500 回答
1

解释真的很简单。的默认值是result.r1多少?什么类型r1?它是 a int,a 的默认intzero。所以当这if (ta != null)没有发生时,意思tanull,你的代码什么也不做。“什么都没有”转化为保留r1其默认值 - 即(您现在已经知道)zero。因此,当ta == null(并且隐含地a == null)您离开r10,尽管您没有明确地这样做。

解决方案很简单:

@Actor
public void reader(I_Result result) {
    A ta = a;
    if (ta != null) {
        result.r1 = ta.f;
    } else {
        result.r1 = -1;
    }
}

和:

@JCStressTest
@State
@Outcome(id = "42", expect = Expect.ACCEPTABLE, desc = "42 is OK")
@Outcome(id = "-1", expect = Expect.ACCEPTABLE, desc = "-1 is OK too")

现在您的代码将永远不会显示0,如果您读取a为非空,您将始终读取a.f42。就您的理解而言,是的,所有线程42一旦看到对实例的引用就会看到A- 这是一个JLS保证。

于 2021-05-17T02:20:53.040 回答
1

现在为什么我在输出中看到值 0 ?

a == null(因此result.r1仍然0)在您的reader()方法中时就是这种情况。

当我将字段 a 声明为静态时。我的输出只有 42,这是为什么呢?

您使用 注释FinalField@State因此 JCStressFinalField为每次执行创建一个新实例。
如果a是 中的实例字段FinalField,则它null最初在每次执行中。
如果a是 中的静态字段FinalField,则它在所有执行中共享,并且null仅在第一次执行中。

于 2021-05-16T11:43:54.420 回答