An awesome book on concurrency that all Java programmers ought to read before embarking on anything more complicated than the primordial Hello World application. I cannot help but reiterate here an example on one of my favourite subjects, that of publication safety. A typical (yet unsafe) lazy initialisation is shown below:
public class LazyInitialization {
private static Resource resource;
public static Resource getInstance() {
if (resource == null)
resource = new Resource();
return resource;
}
}
There’s two issues with this approach. The first is easy to spot: if threads A and B call getInstance()
at the same
time, they may both see resource
as null and both proceed in creating a Resource
instance, hence ending up creating
two different instances even though getInstance()
is supposed to always return the one and the same instance.
The second is much more subtle and has to do with publication. Suppose that thread A first invokes getInstance()
and
creates a Resource
instance, then thread B sees that resource
is non-null and returns the resource
reference.
B could observe however the write to the resource
reference by A as occurring before writes to resource
’s fields
by Resource
’s constructors! Hence, B could see a partially constructed Resource
.
One way this can be fixed is by making getInstance()
synchronized. Another way is by changing the lazy
initialization to eager:
public class LazyInitialization {
private static Resource resource = new Resource();
public static Resource getInstance() {
return resource;
}
}
Why does this work? Static initializers are run by JVM at class initialization time, i.e. before any thread has the
chance to use the class. In addition, JVM acquires a lock while doing so, and this same lock is acquired by every
thread at least once to ensure that the class has been loaded, therefore all memory writes made during initialization
are visible to all threads. Any subsequent modifications will need to be synchronized of course between readers and
writers, unless Resource
is immutable.