library
intermediateSingleton Pattern & Thread Safety
Implement thread-safe singletons correctly, and know why the double-checked locking idiom requires volatile.
One instance for the whole application. The difficulty is doing it safely without paying for a lock on every call.
Singleton = a country's president. There's exactly one at any time. The enum approach = it's written in the constitution (bulletproof). Double-checked locking = asking two guards independently and hoping they agree (fragile without proper protocol/volatile).
Key Concepts
1
Use an enum. It is the simplest correct answer.
public enum Registry {
INSTANCE;
private final Map<String, String> data = new ConcurrentHashMap<>();
public void put(String k, String v) { data.put(k, v); }
}2
The JVM initialises it once, under its own lock, and serialization and reflection cannot produce a second
instance. Nothing else gives you all three.
3
The static holder is the next best, and the one to use when you need a real class:
4
public class Registry {
private Registry() { }
private static class Holder {
static final Registry INSTANCE = new Registry();
}
public static Registry getInstance() { return Holder.INSTANCE; }
}5
Holder is not loaded until getInstance() first runs, so it is lazy. Class initialisation is already
thread-safe, so there is no synchronisation to write and none to pay for.
HoldergetInstance()
6
Double-checked locking is the interview question.
private static volatile Registry instance;7
public static Registry getInstance() {
if (instance == null) { // no lock in the common case
synchronized (Registry.class) {
if (instance == null) // check again, holding the lock
instance = new Registry();
}
}
return instance;
}8
volatile is not optional here. new Registry() is three steps — allocate, run the constructor, assign
the reference — and the JVM may reorder the last two. Without volatile, another thread can see a non-null
reference to an object whose constructor has not finished, and read fields that are still zero.
volatilenew Registry()
9
That reordering is legal, and it broke this idiom entirely before the Java 5 memory model gave volatile its
ordering guarantee.
volatile
10
The weaker options.
synchronized getInstance() correct, locks on every call
static final INSTANCE = ... correct, never lazy11
In practice you rarely write one. A Spring @Component is a singleton managed by the container, which
also makes it injectable and replaceable in a test — the thing a hand-rolled singleton makes hard.
@Component
12
What the interviewer is probing.1. "Why does double-checked locking need volatile?" Probing: reordering, which is the real
answer. Stalls: "For visibility." Moves up: object construction is allocate, construct, assign,
and the last two can be reordered — so another thread can see a non-null reference to a half-built
object.
13
2. "What is the simplest correct singleton?" Probing: whether you reach for the enum.
Stalls: "A static field with a null check." Moves up: an enum with one constant — thread-safe by
class initialisation, and safe against serialisation and reflection, which nothing else gives you.
14
3. "How does the holder idiom achieve laziness without locking?" Probing: class initialisation
semantics. Stalls: "It uses a lock internally." Moves up: the inner class is not loaded until
first referenced, and class initialisation is already thread-safe, so the JVM does the work.
15
4. "Why do you rarely write one in a Spring application?" Probing: context. Stalls: "You
still do." Moves up: a container-managed bean is already a singleton, and it stays injectable and
replaceable in tests, which a hand-rolled one makes hard.