"Make this Singleton thread-safe" is one of the most common concurrency follow-ups in an LLD round — a ParkingLot, Logger, or config registry is often a Singleton, and the naive lazy version is broken under concurrency. Know the five idioms, their trade-offs, and which to recommend.
The broken baseline
class Lot {
private static Lot instance;
static Lot get() {
if (instance == null) // T1 and T2 both see null...
instance = new Lot(); // ...and both construct -> two instances
return instance;
}
}
The check-then-act race again: two threads can each create an instance.
1. Eager initialization
Create it at class-load time. Simple and thread-safe (the classloader guarantees it), but you pay the construction cost even if it's never used.
class Lot {
private static final Lot INSTANCE = new Lot(); // built eagerly
private Lot() {}
static Lot get() { return INSTANCE; }
}
2. Synchronized accessor
Lazy and correct, but every call acquires the lock — a bottleneck on a hot path even though the lock only matters on the first call.
static synchronized Lot get() {
if (instance == null) instance = new Lot();
return instance;
}
3. Double-checked locking (with volatile)
Lazy and fast: lock only on the first construction, lock-free afterward. The volatile is not optional — without it, another thread can see a non-null but partially-constructed object due to instruction reordering.
class Lot {
private static volatile Lot instance; // volatile is REQUIRED
private Lot() {}
static Lot get() {
if (instance == null) { // 1st check (no lock)
synchronized (Lot.class) {
if (instance == null) // 2nd check (locked)
instance = new Lot();
}
}
return instance;
}
}
4. Bill Pugh / initialization-on-demand holder
The idiom most engineers recommend: lazy, thread-safe, and lock-free — with no volatile or synchronized. The holder class isn't loaded until get() first touches it, and class initialization is guaranteed thread-safe by the JVM.
class Lot {
private Lot() {}
private static class Holder { static final Lot INSTANCE = new Lot(); }
static Lot get() { return Holder.INSTANCE; } // lazy + safe + no locks
}
5. Enum singleton
The simplest fully-correct option (Effective Java's recommendation). Thread-safe by construction and immune to reflection and serialization attacks that can break the others.
enum Lot {
INSTANCE;
// fields and methods here
}
Which to use
Need serialization-/reflection-proof + simplest .... ENUM Want lazy, lock-free, idiomatic ..................... BILL PUGH HOLDER Asked to demonstrate concurrency knowledge ......... DOUBLE-CHECKED (volatile) Cheap to construct, always used .................... EAGER Avoid in production ................................. SYNCHRONIZED ACCESSOR (slow)