Why Singleton is the most-used and most-misused pattern in interviews: double-checked locking and why volatile matters, the enum alternative, and when it's an anti-pattern.
Published September 22, 2026
Singleton guarantees a class has exactly one instance and gives it a global access point. It's the first pattern most people learn — and the one most people implement incorrectly under interview pressure.
public class ConfigManager {
private static ConfigManager instance;
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
}
This is not thread-safe. Two threads can both pass the null check before either assigns instance, producing two separate instances — silently, with no exception, only visible under load.
public class ConfigManager {
private static volatile ConfigManager instance;
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
}
Checking instance == null twice (once outside the lock, once inside) avoids paying the synchronization cost on every call — only the first, racing calls ever enter the lock.
The volatile keyword is the detail that separates a correct answer from a memorized one. Without it, another thread can observe a partially constructed ConfigManager — the JVM is allowed to reorder the constructor's writes and the reference assignment, so a reader could see a non-null reference to an object whose fields aren't fully initialized yet. volatile inserts the memory barrier (a happens-before edge) that prevents that reordering, guaranteeing any thread that sees a non-null instance also sees a fully constructed one.
public enum ConfigManager {
INSTANCE;
public void loadConfig() { /* ... */ }
}
The JVM guarantees a class is initialized lazily and thread-safely on first access, and guarantees exactly one instance per enum constant — at the language level, not through code you have to get right yourself. It's also immune to two attacks double-checked locking isn't: reflection can call a private constructor directly (Constructor.setAccessible(true)), and naive serialization can deserialize a second instance — neither works against an enum singleton, because the JVM enforces single-instance-per-constant as a language guarantee, not an implementation detail.
Reasonable uses: truly global, stateless (or read-mostly) resources — a configuration loader, a connection pool manager, a logging facade.
Where it becomes an anti-pattern:
ConfigManager.getInstance() internally has an invisible dependency that doesn't show up in its constructor signature, making the coupling hard to see from the outside.getInstance() call the way you could with a constructor-injected dependency (see Dependency Inversion).In most Spring applications, a @Service-annotated bean already gives you a container-managed singleton (one instance per application context) and keeps the class constructor-injectable and testable — which is why hand-rolled Singleton in application code is increasingly rare outside of infrastructure-level utility classes.
Q: Is the enum singleton always better than double-checked locking?
A: For most practical purposes, yes — it's shorter, immune to reflection/serialization attacks, and the JVM handles thread safety for you. Double-checked locking is worth knowing because interviewers use it to test whether you understand volatile and the JMM, not because it's the recommended way to write a singleton today.
Q: What's the 'initialization-on-demand holder' pattern, and is it better than double-checked locking?
A: A static nested class whose only field is the singleton instance — the JVM's own class-initialization guarantees (lazy, thread-safe) do the synchronization work implicitly, with no explicit lock or volatile needed. It's arguably cleaner than double-checked locking for exactly that reason, though enum singleton remains the most attack-resistant option of the three.
Q: Can Spring's singleton-scoped bean have the same hidden-dependency problem as a hand-rolled Singleton?
A: Only if code reaches for it via a static accessor or ApplicationContext.getBean() instead of constructor injection — the container's singleton scope isn't the problem, it's whether the dependency is made explicit through the constructor or hidden behind a global lookup.