If you remember five things
The whole pattern in one glance. Everything below defends these five calls.
- Intent. Guarantee exactly one instance of a class and give everyone one clean way to reach it, so the class owns its own "how many of me exist" policy.
- Two moves. Private constructor (nobody can call new) plus a static accessor (getInstance) that hands back the one stored reference.
- Write the holder idiom. A static inner holder class gives you lazy, thread-safe init with no synchronized and no volatile, because the JVM initializes a class under its own lock.
- The classic gotcha. Double-checked locking is broken without volatile: a second thread can read a non-null reference to a half-built object.
- When to walk away. Most of the time you want dependency injection, not a Singleton. A Singleton is a dressed-up global, and globals hide dependencies and fight your tests.
1The problem it solves#
Some things in a system should exist exactly once, and letting the rest of the code make copies is where the bugs come from.
- A logger that owns one file handle, one format, one flush policy.
- A connection pool that owns a fixed set of expensive DB connections, say ten of them.
- An app-wide config loaded from disk once at startup, the single source of truth.
What goes wrong when five parts of the code each call new:
- Five log files instead of one, so your output is scattered.
- Five pools fighting over the same ten connections, so you blow the DB's connection cap.
- Five readings of a config that was supposed to be authoritative, so state drifts and you lose an afternoon chasing a ghost.
So the problem is two things at once:
- Guarantee only one instance ever exists.
- Give everyone a clean, global way to reach it without threading the object through every constructor in the codebase.
Why a normal class cannot promise this.
- Anyone can call
new ConnectionPool()as many times as they like. - Singleton closes that door by making the class itself responsible for how many of itself exist.
- That one idea sits underneath everything else on this page.
Singleton ensures a class has exactly one instance and provides a global access point to it. If you can say that cleanly and then name a real case where "there can only be one" is a rule of the domain, you have the pattern. Everything else is thread safety and judgment.
2The idea (mental model)#
Here is how I hold it in my head.
- A regular class is a cookie cutter. Call
new, get a fresh cookie, repeat forever. - A Singleton is a vending machine with one item in stock. Press the button as the first person or the thousandth, you get the exact same object back.
Two moves make it work:
- Lock the front door. Make the constructor private so no outside code can call
new. - Open one window. Expose a static method (usually
getInstance()) that hands back the single shared instance, creating it the first time if needed.
That is the whole trick: private constructor, static accessor, one stored reference. Everything else is detail about timing and thread safety. It is also a clean example of encapsulation: the class controls its own instantiation policy instead of trusting every caller to behave.
3Structure#
The class holds a static reference to its own single instance, the constructor is private, and a public static method returns that instance. Clients never touch the constructor.
ConfigManager holds a reference to one ConfigManager, and every other class reaches it through getInstance(). That self-pointing arrow is the tell that you are looking at a Singleton and not just a static utility class.Here is the runtime flow the first few times someone asks for the instance:
4Implementation in Java#
Let me show the version I actually want to see: thread-safe, lazy, and short. This is double-checked locking, and the detail people forget is volatile.
public class ConfigManager {
private static volatile ConfigManager instance;
private final Map<String, String> settings;
private ConfigManager() {
settings = loadFromDisk();
}
public static ConfigManager getInstance() {
if (instance == null) { // first check, no lock
synchronized (ConfigManager.class) {
if (instance == null) { // second check, with lock
instance = new ConfigManager();
}
}
}
return instance;
}
public String get(String key) {
return settings.get(key);
}
private Map<String, String> loadFromDisk() {
// read config file once at startup
return new HashMap<>();
}
}
Why two null checks:
- The outer check keeps the fast path lock-free once the instance exists, so you are not paying for synchronization on every call.
getInstance()can run millions of times. - The inner check, guarded by the lock, makes sure only one thread actually builds the object even if two both see
nulland both enter.
Why volatile is not optional.
- Without it, a second thread can see a partially constructed object.
- The JVM is allowed to reorder the write that publishes the reference ahead of the writes that fill in the object's fields.
- So a second thread reads a non-null
instancewhosesettingsis still null, and blows up intermittently. - That one word is the difference between correct and subtly broken, and it is the single most common thing candidates drop.
Reveal the answer
If you want something simpler and you do not mind the instance being created when the inner class loads, there is a cleaner trick I prefer for most cases: the initialization-on-demand holder idiom.
public class ConfigManager {
private ConfigManager() { }
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
}
Why this one is my default:
- The JVM loads and initializes a class lazily and thread-safely, because class initialization runs under a lock the JVM owns.
- The inner
Holderclass does not load untilgetInstance()is first called, so you get lazy init and thread safety for free. - No
synchronized, novolatile, no double-check dance. - Less to get wrong, and "less to get wrong" is a senior instinct worth showing.
Double-checked locking
- Lazy
- Yes, built on first call.
- Thread-safe
- Only if volatile is present.
- Easy to get wrong
- Yes: drop volatile and it breaks intermittently.
- Use when
- You are forced to and want to show you know the trap.
Static holder idiom
- Lazy
- Yes, holder loads on first access.
- Thread-safe
- Yes, guaranteed by the JVM class-init lock.
- Easy to get wrong
- No: nothing to synchronize by hand.
- Use when
- Almost always, if you need a lazy Singleton at all.
5A real-world example#
Three places this shows up for real.
- Logging. Most frameworks hand you a single shared logger (or one factory that owns the loggers) so all output goes through one place that controls the file handle, the format, and the flush. You do not want every class opening its own descriptor to the same path.
- Connection pools. Opening a DB connection is slow and the database caps how many you can hold. Create one pool at startup that owns, say, ten connections, and every part of the app borrows and returns from it. One pool per service and you blow past the limit in minutes with "too many connections" errors in production.
- LLD problems. The central controller in a parking lot design, the game board in tic-tac-toe or chess, the config and ID-generator helpers in almost any system. Anything genuinely "the one and only" in the domain is a candidate.
6When to use it, and when not to#
Use it when the thing truly is unique and shared access is the point.
- A hardware device driver.
- A single in-memory cache.
- A config loaded once.
- The rule is "there can only be one" as a property of the domain, not a convenience.
Do not use it just to get a global variable. This is the trap, and the one I watch for.
- Singleton is often a dressed-up global, and globals make code hard to reason about: any part of the system can read and mutate shared state from anywhere, with no signature telling you it happened.
- If you reach for it because passing the object around feels tedious, that is a smell.
- Prefer passing the dependency in through the constructor (dependency injection). The code gets easier to test and to follow, and the Dependency Inversion Principle from SOLID is on your side.
Avoid it for anything you will want to swap or fake in tests.
- A Singleton with a private constructor and static access is painful to mock.
- If a class grabs
ConfigManager.getInstance()deep inside a method, your test cannot hand it a fake config without extra machinery like a static mocking library or a reset hook. - That hidden dependency is the real cost people underestimate, and it compounds across a codebase.
Reaching for Singleton as a convenient global and never mentioning the downsides. If you cannot name the testing pain, the hidden dependencies, and dependency injection as the alternative, you have shown me a reflex, not judgment. My rule: if a senior engineer would object to a global variable here, they will object to this Singleton too.
Make uniqueness a property of the problem, not just something that was convenient to type.
7Common mistakes#
Forgetting thread safety.
- The naive
if (instance == null) instance = new ...()with no synchronization looks fine in one thread and creates two instances the moment two threads hit it at once. - In an interview, if you write the lazy version, address concurrency out loud before I have to ask.
Dropping volatile in double-checked locking.
- The one detail that separates people who memorized the shape from people who understand it.
- Without it the pattern is broken on real JVMs under the Java Memory Model, and it fails intermittently, which is the worst kind of failure.
Not handling serialization and reflection. A determined caller can deserialize a second copy, or use reflection to call setAccessible(true) on the private constructor. For ironclad guarantees, an enum Singleton is the sturdiest option in Java:
public enum ConfigManager {
INSTANCE;
public String get(String key) {
return settings.get(key);
}
private final Map<String, String> settings = loadFromDisk();
private static Map<String, String> loadFromDisk() {
return new HashMap<>();
}
}
- The enum form is safe against serialization and reflection, because the language blocks reflective construction of enum constants and handles deserialization specially.
- The JVM gives you the single-instance guarantee for free.
- It reads oddly the first time, but it is the most bulletproof version, and Joshua Bloch recommends it in Effective Java for exactly these reasons.
When I push on "what if someone reflects into the private constructor," I am not expecting enum by default. I am checking whether you know the plain versions have a gap at all. Naming the gap and reaching for enum when the guarantee actually matters is the full-credit answer. Reaching for enum on every Singleton is overkill and I will say so.
Doing heavy work in the constructor without thinking about when it runs.
- If your Singleton reads a file or hits the network on first access, know whether that happens at startup or on first use, and whether that latency hurts.
- Eager creation moves the cost to boot time; lazy creation moves it to the first unlucky request.
Hidden coupling everywhere.
- Code littered with
getInstance()calls looks clean but is tightly bound to that one concrete class. - You cannot tell from a method's signature that it depends on the config.
- Keep this in mind even when you do use the pattern, and prefer passing the instance in at the edges of your system.
8Where it shows up in interviews#
Three ways, in my experience.
- As a direct "implement a thread-safe Singleton." The warm-up. Write the static holder or double-checked-locking version, explain the thread safety, mention the enum variant if they push on edge cases. A complete answer in about five minutes.
- As a piece of a larger LLD problem. You are designing a parking lot, a vending machine, or a rate limiter, and one component is naturally single: the controller, the config, the ID generator. Using Singleton there and saying why shows judgment rather than reflex.
- As "when would you not use it." This is the one that separates candidates. Explain the downsides (testing pain, hidden dependencies, global state) and offer dependency injection as the alternative, and you have shown you understand the pattern instead of reciting it. Plenty of people write the code; fewer tell me when to walk away, and that answer often moves a feedback from "hire" to "strong hire."
A short script I like: "I would use the static holder idiom for simple, lazy, thread-safe init. If I needed protection against reflection and serialization I would use an enum. And honestly, for most app-level dependencies I would skip Singleton entirely and inject the object, because it tests better." Say something like that and I am satisfied.
"Why does double-checked locking need volatile?"
"Which implementation would you actually ship, and why?"
"When would you not use a Singleton at all?"
"How can someone break your Singleton, and how do you stop them?"
"Eager or lazy initialization?"
What you can now explain in the room
- The one-sentence intent: exactly one instance plus a global access point, with the class owning its own instantiation policy.
- The two moves (private constructor, static accessor) and why a normal class cannot make the guarantee.
- Why the holder idiom is lazy and thread-safe for free, and when double-checked locking is worth the fiddle.
- Why double-checked locking is broken without volatile, in terms of reordering and visibility.
- Why the enum form is the sturdiest against reflection and serialization, and why it is usually overkill.
- When to walk away and inject the dependency instead, and the real cost of hidden global state.
If this clicked, look at the Factory and Builder patterns next; they pair naturally with Singleton when you are controlling how objects get created in an LLD design.
Get a prep plan built around your Singleton Pattern interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.