Singleton Pattern
Guarantee one instance, provide global access. A foundational creational pattern โ the first pattern every developer learns, and the first one they misuse.
Why Singleton Exists
Imagine you are building an application logger. Every module โ authentication, payments, notifications โ needs to write log entries. The naive approach: each module creates its own Logger instance. Now you have three separate loggers, three separate file handles, three separate buffers. Log entries interleave unpredictably, files get corrupted, and there is no single place to change the log level at runtime. Worse, if the logger holds an expensive resource like a database connection pool, you have tripled your memory footprint for no reason.
What goes wrong:
- Resource duplication โ three file handles to the same file; three connection pools to the same database
- Inconsistent state โ one logger sets level to DEBUG, another stays at INFO โ behaviour diverges
- Concurrency corruption โ multiple instances writing to the same file without coordination causes garbled output
- No single control point โ changing log format or level requires hunting down every instance across the codebase
This is the problem Singleton solves โ guarantee exactly one instance of a class exists and give every part of the application a single, coordinated way to reach it.
What Is Singleton?
Singleton is a creational pattern that restricts a class to exactly one instance and provides a global access point to it. The class itself controls its own instantiation โ the constructor is private, and a static method returns the sole instance, creating it lazily on the first call.
— Gamma, Helm, Johnson, Vlissides (1994)
Key insight: The class is responsible for ensuring its own uniqueness. Callers don’t need to check whether an instance exists โ they simply call getInstance() and always receive the same object. The control lives in the class, not in the client code.
newgetInstance() method is the only entry pointParticipants & Structure
| Participant | Role | In the Analogy |
|---|---|---|
| Singleton | The class that controls its own instantiation. Stores the single instance in a private static field and exposes it via a static getInstance() method. The constructor is private. | The office of the president — exactly one exists, and it controls who occupies it. |
| Client | Any code that needs the shared instance. Calls Singleton.getInstance() instead of new Singleton(). | A citizen or ministry — they reference the president’s office, they don’t create a new one. |
- Singleton is the simplest GoF pattern structurally — one class, one static field, one static method.
- The complexity lives in the implementation (thread safety, serialization, reflection), not in the number of participants.
The Pattern In Motion
Scenario: An application logger used by AuthService and PaymentService.
Logger.getInstance()instance is null. The private constructor runs — a new Logger is created, stored in the static field, and returned.logger.log("User logged in")Logger.getInstance()logger.log("Payment processed")- The pseudocode above is NOT thread-safe.
- If two threads call
getInstance()simultaneously wheninstanceis still null, both pass the null check and create separate instances. - Section 6 shows the three safe approaches: synchronized, double-checked locking with volatile, and the enum idiom.
You Already Use This
You already use Singleton every time you call Runtime.getRuntime(). The JVM creates exactly one Runtime object โ there is no public constructor. Several other JDK classes follow the same pattern.
java.lang.Runtime.getRuntime() — returns the sole Runtime object. You cannot instantiate Runtime directly; the JVM creates exactly one, and getRuntime() returns it. Used for memory stats, GC triggers, and process execution. java.awt.Desktop.getDesktop() — returns the singleton Desktop instance for the current platform. Opening a file, browsing a URL, and composing mail all go through this one object. java.util.logging.Logger.getLogger("name") is NOT a Singleton — it returns different instances for different names. That’s a Registry (Multiton) pattern, not Singleton. Desktop.getDesktop()throwsUnsupportedOperationExceptionon headless servers.- Check
Desktop.isDesktopSupported()first. - This is a real-world example of why Singletons sometimes need a guard โ the “single instance” may not exist at all on certain platforms.
Build It Once
Domain: Application Logger. Thread-safe, lazy-initialized, with configurable log level. We show the double-checked locking approach here — the modal includes the enum alternative and a Python equivalent.
- Use enum when you need zero-config safety (serialization, reflection, thread-safe).
- Use DCL + volatile when you need lazy initialization with constructor arguments.
- Use Bill Pugh holder (static inner class) when you want lazy init without synchronized blocks.
- Avoid plain lazy init (no sync) — it is broken in multithreaded code.
Common Mistakes
volatile: This is the most critical Singleton bug. Without volatile, a thread can see a non-null instance reference before the constructor has finished executing — the JVM is allowed to reorder the write of the reference and the initialisation of the object’s fields. The result: one thread gets a half-constructed object with null fields.
- If your Singleton accumulates unrelated state — config + DB pool + cache + metrics — it’s a God Object, not a design pattern.
- A Singleton should encapsulate exactly one responsibility.
- If you find yourself passing the Singleton to every method, you’ve created a Service Locator anti-pattern.
- Consider dependency injection instead.
ObjectInputStream.readObject() creates a new instance bypassing your private constructor. So does Constructor.setAccessible(true). Defences:
- Implement
readResolve()to return the existing instance - Or use an enum Singleton — the JVM guarantees it is serialization-safe and reflection-safe
- Code that calls
Logger.getInstance()directly cannot swap in a mock logger for testing. - The fix: depend on an interface (
ILogger) and inject the implementation. - This lets you use Singleton lifecycle management in production while injecting a mock in tests.
When To Use Singleton
- You need exactly one instance of a class — logger, configuration holder, connection pool, thread pool manager
- The instance must be accessible globally — any module can reach it without being handed a reference
- Creating multiple instances would cause resource conflicts — two writers to the same file, two pools to the same database
- The instance needs to coordinate shared mutable state — a central event bus, a cache, or a service registry
- You need lazy initialization — the expensive object should only be created if actually used
- You need multiple instances — e.g. different loggers per module → use a Registry / Multiton instead
- You need testability — code tightly coupled to
getInstance()is hard to mock → use dependency injection - You are using it as a global variable bag — if the Singleton has 10 unrelated fields, refactor into multiple classes
- Your framework already handles lifecycle — Spring
@Beandefaults to singleton scope — no need to hand-roll it
| Approach | Instances | Thread Safety | Testability | Best For |
|---|---|---|---|---|
| Singleton ← this | Exactly 1 | You manage it | Hard (needs interface) | Logger, Config, Registry |
| Dependency Injection | Configurable | Framework-managed | Easy (swap impl) | Modern apps, Spring/Guice |
| Static Utility Class | 0 (all static) | Stateless only | Hard (no polymorphism) | Pure functions (Math, Collections) |
| Prototype (clone) | Many (copies) | Per-copy | Easy | Expensive-to-create templates |
Problems To Solve
Singleton interview questions test thread safety, lazy initialization trade-offs, and your understanding of when not to use the pattern. Interviewers want to see you discuss trade-offs — not just write getInstance().
| Difficulty | Problem | Key Insight |
|---|---|---|
| Easy | Thread-Safe Logger Implement a Singleton Logger that is thread-safe and writes timestamped messages to the console. | Use volatile + double-checked locking, or the enum approach. The interviewer checks: do you know why volatile is required? (instruction reordering) |
| Medium | Connection Pool Manager Design a Singleton that manages a fixed-size database connection pool. getConnection() blocks if all connections are in use. | The Singleton owns a BlockingQueue<Connection>. Key: Singleton ensures one pool; the queue handles concurrency of borrowing/returning connections. |
| Medium | Application Config (reload-safe) Build a Singleton config holder that reads from a properties file on first access, and can be reloaded at runtime without creating a new instance. | Expose a reload() method that re-reads the file and swaps internal state atomically. Object identity stays the same — all references see the new config. Test: what happens if a thread reads config mid-reload? |
| Hard | Singleton Registry (Multiton) Extend Singleton to support named instances: Logger.getInstance("auth") returns one Logger, Logger.getInstance("payment") returns a different one, but repeated calls with the same name return the same object. | Use a ConcurrentHashMap<String, Logger> with computeIfAbsent() — it atomically checks and creates. This is the Registry/Multiton pattern: “one per key” instead of “one total.” |
- The interviewer isn’t just testing if you can write
getInstance(). - They want you to discuss: (1) Why is
volatileneeded? (2) What breaks with serialization/reflection? (3) When would you prefer DI over Singleton? (4) What’s the enum approach and why is it the safest? - Show that you understand the trade-offs, not just the syntax.