Reputation: 441
I am using JCStress
to test for the final variable. I know that final
can be used to make sure that when you construct an object, another thread accessing that object doesn't see that object in a partially constructed state.
Now I have a class A.java as such
public class A {
final int f;
A() {
this.f = 42;
}
}
According to my knowledge, the constructor should be executed as such
A temp = <new>
temp.f = 42
<freeze value>
fv = temp
Now I'm using the below-mentioned test.
@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;
}
}
}
Now, why is it that I see the value 0 in my output? My CPU architecture is x86, so reordering stores with loads also doesn't make sense. The output I get is
0 94,922,153 FORBIDDEN No default case provided, assume FORBIDDEN
42 48,638,587 ACCEPTABLE Final value initialized
Also, one more thing that I found unusual is that when I declare the field a
as static
. I get only 42 as my output, and why is that?
42 299,477,390 ACCEPTABLE Final value initialized
Upvotes: 3
Views: 118
Reputation: 120988
The explanation is really easy. What is the default value of result.r1
? What type is r1
? It's an int
, and a default value for a int
is zero
. So when this if (ta != null)
does not happen, meaning ta
is null
, your code will do nothing. That "nothing" translates into leaving r1
to its default value - that is (you already know by now) zero
. So when ta == null
(and implicitly a == null
), you leave r1
to be 0
, though you do not do that explicitly.
The solution is trivial:
@Actor
public void reader(I_Result result) {
A ta = a;
if (ta != null) {
result.r1 = ta.f;
} else {
result.r1 = -1;
}
}
and:
@JCStressTest
@State
@Outcome(id = "42", expect = Expect.ACCEPTABLE, desc = "42 is OK")
@Outcome(id = "-1", expect = Expect.ACCEPTABLE, desc = "-1 is OK too")
And now your code will never show 0
, if you read a
to be non-null, you will always read a.f
to be 42
. As far as your understanding goes, yes, all threads will see 42
once they see a reference to an instance of A
- that is a JLS
guarantee.
Upvotes: 1
Reputation: 11
Another answer already explained why you have problems with 0
and static
.
But even with these problems fixed it could be difficult to reproduce partial initialization.
So I would recommend you to take a look at the JCStress source code: it contains samples, and one of them (JMMSample_06_Finals) already does what you want.
Upvotes: 1
Reputation: 11
Now why is it that I see the value 0 in my output ?
These is the case when a == null
(and therefore result.r1
remains 0
) in your reader()
method.
when I declare the field a as static. I get only 42 as my output, and why is that?
You annotated FinalField
with @State
, therefore JCStress creates a new instance of FinalField
for every execution.
If a
is an instance field in FinalField
, then it is null
initially in every execution.
If a
is a static field in FinalField
, then it is shared across all executions and is null
only in the first execution.
Upvotes: 1