我正在使用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 另外,我发现另一件不寻常的事情是,当我将字段a声明为static时。我的产出只有42,这是为什么?
42 299,477,390 ACCEPTABLE Final value initialized 发布于 2021-05-16 11:43:54
为什么我看到输出中的值为0?
当a == null (因此result.r1保持为0)在reader()方法中时,情况就是如此。
当我宣布字段a为静态的时候。我的产出只有42,这是为什么?
您用@State注释了@State,因此JCStress为每次执行创建了一个新的FinalField实例。
如果a是FinalField中的实例字段,那么在每次执行中它最初都是null。
如果a是FinalField中的一个静态字段,那么它在所有执行过程中都是共享的,并且只有在第一次执行时才是null。
发布于 2021-05-16 17:54:50
另一个答案已经解释了为什么您对0和static有问题。
但是,即使解决了这些问题,也很难再现部分初始化。
因此,我建议您查看一下JCStress源代码:它包含示例,其中一个示例(决赛)已经完成了您想要的操作。
发布于 2021-05-17 02:20:53
这个解释真的很简单。result.r1的默认值是什么?r1是什么类型的?它是一个int,int的默认值是zero。因此,当这个if (ta != null)没有发生时,意味着ta是null,您的代码将什么也不做。"nothing“转换为将r1保留为其默认值--即(您现在已经知道) zero。因此,当ta == null (以及隐式a == null)时,您将r1留给0,尽管您没有显式地这样做。
解决方案是微不足道的:
@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.f读取为42。就您的理解而言,是的,一旦看到对A实例的引用,所有线程都会看到A--这是JLS的保证。
https://stackoverflow.com/questions/67553393
复制相似问题