I've got this simplified code snippet with two volatiles (assume we must keep both fields):
volatile boolean hasParam;
volatile String param;
boolean hasParam() {
if (param == null) {
getParam();
}
return hasParam;
}
String getParam() {
String tmp = param;
if (tmp == null) {
tmp = "String";
hasParam = true;
param = tmp;
}
return tmp;
}
I'm speculating whether I can drop volatile
from hasParam
field declaration in this way: if there are two threads calling hasParam()
under race and the first one writes into volatile param
field (doing releasing store) and the second thread reads non-null value from the same field (doing acquiring read) then it must read true
and only true
from hasParam
. In the opposite case (acquiring read reads null
) the second thread writes true
into param
and returns it from hasParam()
.
Basing on this speculation I assume I can safely erase volatile
keyword from hasParam
field declaration.
In order to prove it I've designed this JCStress-based test:
@State
@JCStressTest
@Outcome(id = "true, String", expect = ACCEPTABLE, desc = "Boolean value was flushed")
@Outcome(id = "false, String", expect = FORBIDDEN, desc = "Boolean value was not flushed")
public class ConcurrencyTest {
Value value = new Value();
@Actor
public void actor1(ZL_Result r) {
r.r1 = value.hasParameter();
r.r2 = value.param;
}
@Actor
public void actor2(ZL_Result r) {
r.r1 = value.hasParameter();
r.r2 = value.param;
}
static class Value {
volatile boolean hasParam;
volatile String param;
boolean hasParameter() {
if (param == null) {
getParam();
}
return hasParam;
}
String getParam() {
String tmp = param;
if (tmp == null) {
tmp = "String";
hasParam = true;
param = tmp;
}
return tmp;
}
}
}
This test yields the results:
RESULT SAMPLES FREQ EXPECT DESCRIPTION
false, String 0 0,00% Forbidden Boolean value was not flushed
true, String 638 164 992 100,00% Acceptable Boolean value was flushed
If I erase volatile
from hasParam
field declaration then I get the same results (I assume it proves correctness of my speculation):
RESULT SAMPLES FREQ EXPECT DESCRIPTION
false, String 0 0,00% Forbidden Boolean value was not flushed
true, String 1 074 450 432 100,00% Acceptable Boolean value was flushed
And if I erase volatile
from both field declaration the test fails with:
RESULT SAMPLES FREQ EXPECT DESCRIPTION
false, String 1 420 423 0,12% Forbidden Boolean value was not flushed
true, String 1 164 432 249 99,88% Acceptable Boolean value was flushed
So I've got two questions:
- Is my speculation correct and the program remains correct?
- Is my test correct?