Explain the initialization-on-demand holder idiom and why it is thread-safe without explicit synchronization.
answer
- Private static nested holder with static final INSTANCE
- Class initialized lazily on first active use
- JVM initializes each class exactly once, under a lock
- Safe publication via happens-before from class init
- No volatile / no synchronized needed
- Can't be parameterized at runtime
basics
~20 sYou put the value in a private static nested class. The JVM only loads that class the first time it is used, and class loading is automatically thread-safe, so the value is created lazily and safely without you writing any locks.
solid answer
~50 sThe initialization-on-demand holder idiom is the cleanest way to lazily create a singleton-style value in Java. You declare a private static nested 'holder' class whose only job is a `static final` field holding the value. The outer class's accessor returns `Holder.INSTANCE`. The key is the JVM's class-initialization rules: a nested class is not initialized until it is first actively used — here, the first call to the accessor. So the expensive field is built lazily, on first access. Thread-safety is free because the Java Language Specification requires class initialization to run under a lock the JVM holds, and other threads block until it completes; the JVM guarantees a class is initialized exactly once. You get lazy, thread-safe, lock-free-at-the-call-site singleton creation with no `synchronized`, no `volatile`, and no double-checked locking. The main limitation: it fits values keyed off nothing (a true singleton) and can't take runtime parameters.
go deeper
Can recognize and write the static-nested-holder pattern and state that it gives a lazy, thread-safe singleton.
Explains that lazy class initialization plus the JVM's exactly-once class-init lock provide both laziness and thread-safety, so no volatile/synchronized is needed.
Articulates the safe-publication/happens-before guarantee, compares it to DCL, and knows its limitation (no runtime parameterization, awkward failure retry).
Chooses between holder, DCL, and Supplier/computeIfAbsent based on parameterization, failure semantics, and team-error risk; can justify the choice in a design review.
## The problem this solves You want a value that is (a) created **lazily** (only when first needed) and (b) **thread-safe** (multiple threads can't create it twice or see it half-built), but you don't want to write `synchronized` locks or fiddly double-checked locking. The **initialization-on-demand holder idiom** (sometimes 'lazy holder' or 'Bill Pugh singleton') achieves all three by leaning on a guarantee the Java Virtual Machine (JVM) already gives you for free. ## Background: class loading and initialization In Java, a class isn't fully ready to use the moment your program starts. It goes through loading, linking, and **initialization**. *Initialization* is when its `static` fields get their values and `static` initializer blocks run. The Java Language Specification (JLS) says a class is initialized **lazily** — only on its **first active use**, such as the first time you read one of its non-constant static fields or create an instance. Critically, the JLS also says the JVM **initializes each class exactly once**, and it does so under an internal lock: if thread A is initializing a class and thread B touches it, thread B **blocks** until A finishes, then sees the fully initialized class. This is the load-bearing guarantee. ## The idiom ```java public final class HeavyService { private HeavyService() { /* expensive setup */ } // Nested class is NOT loaded until Holder is first referenced. private static class Holder { static final HeavyService INSTANCE = new HeavyService(); } public static HeavyService getInstance() { return Holder.INSTANCE; // triggers Holder's initialization here, lazily } } ``` Walkthrough: - Loading `HeavyService` does **not** load `Holder` (a nested class is independent for initialization purposes). - `Holder.INSTANCE` is only evaluated when `getInstance()` is first called. - At that moment the JVM initializes `Holder`, which runs `new HeavyService()` — the expensive work, done **once**, **lazily**. - Because class initialization is serialized by the JVM's lock and happens exactly once, **no two threads can build two instances** and nobody sees a partial object. The first caller pays the cost; others block until it's ready, then all see the same instance. ## Why no `volatile` or `synchronized`? The **safe-publication** problem (one thread building an object, another seeing it stale or half-constructed) is handled by the JVM's class-init machinery, which establishes the necessary memory ordering (a *happens-before* relationship between the writes in the initializer and every later read of the static field). You don't need `volatile` on the field or a `synchronized` accessor because the language already guarantees what they'd provide. ## Strengths - **Lazy** (built on first access), **thread-safe**, **lock-free at the call site** (after init, `getInstance()` is just a field read), and **simple** — no double-checked locking to get subtly wrong. ## Limitations - It produces **one** value per holder — great for a singleton, but it can't be **parameterized** at runtime (you can't pass an argument to choose which instance). For per-key lazy values you'd use a `ConcurrentHashMap.computeIfAbsent` or a memoizing `Supplier`. - If construction can **fail** (throw), the failure surfaces as an `ExceptionInInitializerError` on first access and the class stays uninitialized in a broken way — recovery/retry is awkward. For fail-and-retry semantics, a `Supplier`-based or DCL approach is more flexible. ## Contrast with double-checked locking (DCL) DCL achieves the same lazy+thread-safe goal but requires a `volatile` field and careful code; it exists mainly for the parameterized/instance-field cases the holder idiom can't cover. For a plain static singleton, the holder idiom is preferred precisely because the compiler/JVM, not you, owns the correctness.
- Why isn't a volatile field needed on Holder.INSTANCE?Because the JVM's class-initialization protocol already establishes a happens-before relationship: the writes performed while initializing Holder are guaranteed visible to any thread that later reads Holder.INSTANCE. That's exactly the safe publication volatile would provide, so it's redundant.
- What can the holder idiom NOT do that double-checked locking can?It can't produce a lazily-built value that depends on runtime parameters or lives in an instance field. A static holder yields exactly one value tied to the class. For parameterized or per-instance lazy values you use DCL, computeIfAbsent, or a memoizing Supplier.
saying these in an interview costs you the question
- Saying it needs synchronized or volatile to be safe (the whole point is it doesn't)
- Claiming the holder class loads at the same time as the outer class (it loads on first use of the holder)
- Using it for per-key/parameterized lazy values, which it can't express
- Confusing class loading with class initialization — it's initialization that's lazy and serialized