What is the initialization-on-demand holder idiom, and why is it often preferred over double-checked locking for lazy singletons?
answer
- Instance lives in a private static nested holder class
- JVM initializes a class lazily, on first active use
- Class init is once + thread-safe (per-class init lock)
- No volatile, no synchronized in your code
- Doesn't fit when construction needs runtime args
basics
~20 sYou put the singleton in a private static nested 'holder' class that holds the instance in a static field. The JVM only loads and initializes that nested class the first time you touch it, and the JVM guarantees class initialization is thread-safe — so you get lazy, safe initialization with no locks or volatile.
solid answer
~40 sThe initialization-on-demand holder idiom places the singleton instance in a private static nested class whose only job is to hold it. The outer class can be loaded without the nested holder being initialized — the JVM only initializes the holder class the first time it is actively used, which happens when `getInstance()` references `Holder.INSTANCE`. This gives lazy initialization for free. Crucially, the JVM specification guarantees that class initialization runs under an internal lock and exactly once, with proper happens-before visibility to all threads. So you get thread-safe lazy init without writing any `synchronized` block or `volatile` field. It is preferred over double-checked locking because it is simpler, has no per-call locking on the hot path (the JVM's init lock is taken only once), and removes the chance of getting the subtle volatile/reordering details wrong.
code
java · 12 linespublic final class ConfigCache {
private ConfigCache() { /* expensive build */ }
// Nested class is not initialized until first referenced:
private static final class Holder {
static final ConfigCache INSTANCE = new ConfigCache();
}
public static ConfigCache getInstance() {
return Holder.INSTANCE; // JVM guarantees once + visible to all threads
}
}go deeper
Can write the nested-holder pattern and state that it is lazy and thread-safe.
Explains both JVM guarantees it relies on (lazy class init on first use + thread-safe once-only initialization) and why that removes the need for volatile/synchronized.
Compares it cleanly to DCL (no hot-path lock, no reordering pitfalls) and knows its limit with parameterized construction and exceptional initialization.
Chooses among holder idiom, enum singleton, eager static, and DI-container scoping based on laziness needs, serialization/reflection safety, and testability, and can justify the choice on a team.
## The idiom ```java public final class Singleton { private Singleton() { } private static class Holder { // private static nested class static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; // first touch triggers init } } ``` ## The two JVM rules it exploits **Rule 1 — lazy class initialization.** A class in Java is *loaded* and *initialized* separately. The JVM only **initializes** a class (running its static field initializers and static blocks) the first time the class is *actively used* — for example, when one of its static fields is first read. The nested `Holder` class is never touched while the outer `Singleton` class loads; it is only initialized the first time `getInstance()` evaluates `Holder.INSTANCE`. So construction is naturally **lazy**: if nobody ever calls `getInstance()`, the singleton is never built. **Rule 2 — class initialization is thread-safe.** The Java Language Specification requires that class initialization is performed under a per-class **initialization lock**, runs **exactly once**, and that its effects are visible to every thread that uses the class afterward (it establishes happens-before). If two threads call `getInstance()` at the same time and the holder isn't initialized yet, the JVM makes one thread do the initialization while the others wait, then all threads see the same fully constructed `INSTANCE`. This is the same guarantee that makes ordinary `static final` fields safe — the idiom just *delays* it to first use by hiding the field in a nested class. ## Why it beats double-checked locking - **No `volatile`, no `synchronized` in your code.** You don't hand-write any memory-ordering logic, so you can't get the volatile/reordering subtleties wrong — the very thing that makes DCL error-prone. - **No per-call lock on the hot path.** With DCL you at least re-check a volatile field every call; with the holder idiom, after the class is initialized, `getInstance()` is just a plain static field read with zero synchronization. The JVM's init lock is paid exactly once. - **Simpler and obviously correct.** It is short, idiomatic, and its correctness rests on a JVM guarantee everyone already relies on for static fields. ## Limitations - It works only when the instance can be created from information available at class-init time. If construction needs **runtime parameters** (e.g. a config object passed into `getInstance(config)`), a static field initializer can't take arguments, so the holder idiom doesn't fit and you fall back to DCL or another approach. - If the constructor can fail, a thrown exception during class initialization turns the class into an unusable `ExceptionInInitializerError`/`NoClassDefFoundError` state — consider whether eager failure is acceptable. ## Relation to the enum singleton Joshua Bloch's *Effective Java* notes a single-element **enum** is often the best singleton: it is concise, serialization-safe, and reflection-safe. But an enum is eagerly initialized when the enum class loads, so when you specifically need *laziness*, the holder idiom is the go-to.
- What exactly triggers the holder class to initialize?The first active use of the holder class — here, the first time `getInstance()` reads `Holder.INSTANCE`. Loading the outer `Singleton` class does not initialize the nested `Holder`, which is what makes the singleton lazy.
- Why doesn't this need volatile like DCL does?Because the JVM performs class initialization under its own internal lock with a happens-before guarantee to all subsequent users of the class. You inherit thread-safe publication from the JVM instead of hand-rolling it, so no volatile or synchronized is required in your code.
- When can't you use the holder idiom?When the singleton's construction depends on runtime parameters passed to the getter, since a static field initializer takes no arguments; or when you genuinely want eager initialization, where a static field or enum is simpler.
saying these in an interview costs you the question
- Claiming the holder is initialized when the outer class loads (it's initialized on first reference)
- Saying it still needs a volatile field or synchronized block
- Believing it works for parameterized construction (static initializers can't take arguments)
- Confusing it with the eager enum singleton when laziness is the requirement