A recent question here had the following code (well, similar to this) to implement a singleton without synchronisation.
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Now, I think understand what this is doing. Since the instance is static final
, it's built long before any threads will call getInstance()
so there's no real need for synchronisation.
Synchronisation would be needed only if two threads tried to call getInstance()
at the same time (and that method did construction on first call rather than at "static final"
time).
My question is therefore basically: why then would you ever prefer lazy construction of the singleton with something like:
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
My only thoughts were that using the static final
method may introduce sequencing issue as in the C++ static initialisation order fiasco.
First off, does Java actually have this problem? I know order within a class is fully specified but does it somehow guarantee consistent order between classes (such as with a class loader)?
Secondly, if the order is consistent, why would the lazy construction option ever be advantageous?
Just a little note about the first implementation: the interesting thing here is that class initialization is used to replace classical synchronization.
Class initialization is very well defined in that no code can ever get access to anything of the class unless it is fully initialized (i.e. all static initializer code has run). And since an already loaded class can be accessed with about zero overhead, this restricts the "synchronization" overhead to those cases where there is an actual check to be done (i.e. "is the class loaded/initialized yet?").
One drawback of using the class loading mechanism is that it can be hard to debug when it breaks. If, for some reason, the
Singleton
constructor throws an exception, then the first caller togetInstance()
will get that exception (wrapped in another one).The second caller however will never see the root cause of the problem (he will simply get a
NoClassDefFoundError
). So if the first caller somehow ignores the problem, then you'll never be able to find out what exactly went wrong.If you use simply synchronization, then the second called will just try to instantiate the
Singleton
again and will probably run into the same problem (or even succeed!).The code in the first version is the correct and best way to safely lazily construct a singleton. The Java Memory Model guarantees that INSTANCE will:
Version 1 is an excellent pattern to follow.
EDITED
Version 2 is thread safe, but a little bit expensive and more importantly, severely limits concurrency/throughput
In Effective Java, Joshua Bloch notes that "This idiom … exploits the guarantee that a class will not be initialized until it is used [JLS, 12.4.1]."
You initialize eagerly because you don't have to write a synchronized block or method. This is mainly because synchronization is generally considered expensive
I'm not into your code snippet, but I have an answer for your question. Yes, Java has an initialization order fiasco. I came across it with mutually dependent enums. An example would look like:
The key is that B.B1 must exist when creating instance A.A1. And to create A.A1 B.B1 must exist.
My real-life use case was bit more complicated - the relationship between the enums was in fact parent-child so one enum was returning reference to its parent, but the second array of its children. The children were private static fields of the enum. The interesting thing is that while developing on Windows everything was working fine, but in production—which is Solaris—the members of the child array were null. The array had the proper size but its elements were null because they were not available when the array was instantiated.
So I ended up with the synchronized initialization on the first call. :-)