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?