Take the following java code:
public class SomeClass {
private boolean initialized = false;
private final List<String> someList;
public SomeClass() {
someList = new ConcurrentLinkedQueue<String>();
}
public void doSomeProcessing() {
// do some stuff...
// check if the list has been initialized
if (!initialized) {
synchronized(this) {
if (!initialized) {
// invoke a webservice that takes a lot of time
final List<String> wsResult = invokeWebService();
someList.addAll(wsResult);
initialized = true;
}
}
}
// list is initialized
for (final String s : someList) {
// do more stuff...
}
}
}
The trick is that doSomeProcessing
gets invoked only under certain conditions. Initializing the list is a very expensive procedure and it might not be needed at all.
I have read articles on why the double-check idiom is broken and I was a bit skeptic when I saw this code. However, the control variable in this example is a boolean, so a simple write instruction is needed, as far as I know.
Also, please notice that someList
has been declared as final
and keeps a reference to a concurrent list, whose writes
happen-before reads
; if instead of a ConcurrentLinkedQueue
the list were a simple ArrayList
or LinkedList
, even though it has been declared as final
, the writes
don't require to happen-before the reads
.
So, is the code given above free of data races?