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?