Java final字段是否保证字段值在不同线程中可见?
我正在使用Java final字段是否保证字段值在不同线程中可见?,java,multithreading,concurrency,jcstress,Java,Multithreading,Concurrency,Jcstress,我正在使用JCStress测试最终变量。我知道final可以用来确保在构造对象时,访问该对象的另一个线程不会看到处于部分构造状态的对象。 现在我有一个类a.java public class A { final int f; A() { this.f = 42; } } 据我所知,构造函数应该这样执行 A temp = <new> temp.f = 42 <freeze value> fv = temp 现在,为什么我在输出
JCStress
测试最终变量。我知道final
可以用来确保在构造对象时,访问该对象的另一个线程不会看到处于部分构造状态的对象。
现在我有一个类a.java
public class A {
final int f;
A() {
this.f = 42;
}
}
据我所知,构造函数应该这样执行
A temp = <new>
temp.f = 42
<freeze value>
fv = temp
现在,为什么我在输出中看到值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
为什么我在输出中看到值0
当reader()
方法中的a==null(因此result.r1
保留0
)时,就是这种情况
当我将字段a声明为静态时。我的输出只有42,这是为什么
42 299,477,390 ACCEPTABLE Final value initialized
您用@State
注释了FinalField
,因此jcstrest为每次执行创建一个新的FinalField
实例。
如果a
是FinalField
中的一个实例字段,则每次执行时它最初都是null
。
如果a
是FinalField
中的一个静态字段,那么它将在所有执行中共享,并且仅在第一次执行时null
。另一个答案已经解释了为什么0
和静态
但即使修复了这些问题,也很难重现部分初始化。
因此,我建议您看看JCStress源代码:它包含示例,其中一个()已经完成了您想要的功能。解释非常简单。result.r1的默认值是什么?什么类型的r1是r1
?它是一个int
,而int
的默认值是zero
。因此,如果(ta!=null)
未发生,则表示ta
为null
,则代码将不起任何作用。“nothing”表示将r1
保留为其默认值,即(您现在已经知道)0
。因此,当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;
}
}
以及:
现在,您的代码将永远不会显示0
,如果将a
读为非空,您将始终将a.f
读为42
。就您的理解而言,是的,所有线程一旦看到对a
实例的引用,就会看到42
——这是JLS
保证。那么,有没有办法测试变量f是否为0(即部分初始化),我从类a中删除了final。我只得到42。
@JCStressTest
@State
@Outcome(id = "42", expect = Expect.ACCEPTABLE, desc = "42 is OK")
@Outcome(id = "-1", expect = Expect.ACCEPTABLE, desc = "-1 is OK too")