I woke up this morning thinking I knew all there was to know about lazy loading singletons in Java. Boy, was I wrong. Java doesn't cease to surprise me, even after all these years.
First, why would you want to lazy load a singleton? In production, you typically want to eagerly load all your singletons so you catch errors early and take any performance hit up front, but in tests and during development, you only want to load what you absolutely need so as not to waste time.
Before Java 1.5, I lazy loaded singletons using plain old synchronization, simple but effective:
static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null)
instance == new Singleton();
return instance;
}
Changes to the memory model in 1.5 enabled the infamous Double-Checked Locking (DCL) idiom. To implement DCL, you check a volatile field in the common path and only synchronize when necessary:
static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance == new Singleton();
}
}
return instance;
}
But
volatile isn't that much faster than
synchronized,
synchronized is pretty fast nowadays, and DCL requires more code, so even after 1.5 came out, I continued using plain old synchronization.
Imagine my surprise today when Jeremy Manson pointed me to the Initialization on Demand Holder (IODH) idiom which requires very little code and has zero synchronization overhead. Zero, as in even faster than volatile. IODH requires the same number of lines of code as plain old synchronization, and it's faster than DCL!
IODH utilizes lazy class initialization. The JVM won't execute a class's static initializer until you actually touch something in the class. This applies to static nested classes, too. In the following example, the JLS guarantees the JVM will not initialize instance until someone calls getInstance():
static class SingletonHolder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
Why didn't IODH register in my brain sooner? I'm on the JMM mailing list after all. I think I had a mental block due to the fact that exceptions thrown in class initializers used to be difficult to debug, i.e. before nested exceptions. If I recall correctly, you would get an
ExceptionInInitializerError (which would tromp the root exception) followed by a series of
ClassNotFoundExceptions. Lazy loading from an application thread didn't suffer this problem.
Today, exceptions in static initializers are easy enough to diagnose--they appear nested in the ExceptionInInitializerError, so use IODH from now qualm free.
Update: Credit where credit is due, Effective Java (copyright 2001) detailed this pattern under item 48. It goes on to point out that you still have to use synchronization or DCL in non-static contexts.
I also switched singleton handling in my framework from synchronization to DCL and saw another 10% performance boost (compared to before I started using cglib's fast reflection). I only used one thread in my micro-benchmark, so the boost to concurrency could be even greater given that I replaced a heavily contended lock with a relatively fine grained volatile field access.