Overview
The Singleton pattern ensures a class has exactly one instance and provides global access to it. Classic uses are configuration, a logger, or a connection pool that must be shared. Implementations use a private constructor and a static accessor, with care for thread-safe lazy initialization.
Singletons are also one of the most criticized patterns: they are hidden global state, make testing harder (you cannot easily substitute a fake), and couple code to a concrete class. Modern designs usually achieve 'one instance' through dependency injection (a singleton-scoped bean) instead of a static getInstance().
There is only one at a time, and everyone refers to 'the president' rather than creating their own. But if every citizen depended directly on one person for everything, replacing them would be chaos, which is the testing problem with singletons.
When to use it
- Exactly one instance is required by the domain (hardware controller, process-wide registry).
- Shared expensive resource (connection pool, cache) — preferably via DI scope.
- Interviewers ask how to implement a thread-safe singleton.
Where it shows up in interviews
Recognize it when: 'implement a singleton' or shared manager in LLD.
- Design a logger
- Design a parking lot manager
- Design a configuration service
Where it is used in real software
The JDK exposes one Runtime per JVM.
Default singleton scope: one instance per container, injected where needed, without static access.
A module's exports are created once and shared by every importer.
Key terms
- Private constructor
- Prevents outside instantiation.
- Lazy initialization
- Create on first use.
- Double-checked locking
- Check, lock, check again; needs volatile in Java.
- Enum singleton
- Java enum with one value: thread-safe and serialization-safe.
- Holder idiom
- Static nested class loaded on first access.
How it works, step by step
- 1Ask if one instance is truly required
Often DI scope is better.
- 2Make the constructor private
No outside new.
- 3Provide a static accessor
getInstance().
- 4Make initialization thread-safe
Holder idiom, enum, or eager init.
- 5Keep it testable
Depend on an interface; allow injection in tests.
Java singleton implementations
Thread safety and laziness
| Approach | Lazy? | Thread-safe? | Notes |
|---|---|---|---|
| Eager static field | No | Yes | Simplest |
| Synchronized getInstance | Yes | Yes | Lock on every call |
| Double-checked locking + volatile | Yes | Yes | Error-prone if volatile is missing |
| Holder idiom | Yes | Yes | JVM class loading guarantees safety |
| Enum | No | Yes | Also safe against reflection and serialization |
NOWApproach: Eager static field | Lazy?: No | Thread-safe?: Yes | Notes: Simplest
In Java, prefer the holder idiom or enum. In most application code, prefer a DI-managed single instance.
Implementation
// Holder idiom: lazy and thread-safe without explicit lockspublic final class AppConfig { private final Map<String, String> values; private AppConfig() { values = loadFromEnvironment(); } private static final class Holder { private static final AppConfig INSTANCE = new AppConfig(); } public static AppConfig getInstance() { return Holder.INSTANCE; } public String get(String key) { return values.get(key); } private static Map<String, String> loadFromEnvironment() { return Map.copyOf(System.getenv()); }} // Enum singletonpublic enum IdGenerator { INSTANCE; private final java.util.concurrent.atomic.AtomicLong next = new java.util.concurrent.atomic.AtomicLong(1); public long nextId() { return next.getAndIncrement(); }}Complexity and performance
Static field.
JVM guarantees.
Trade-offs
Hidden dependencies make code harder to reason about and test.
A static singleton is one per classloader or process, not per cluster; distributed systems need other coordination.
Variants and related techniques
Container creates one instance and injects it.
One instance per key (per tenant or region).
Common mistakes
- Double-checked locking without volatile.
Fix: Use the holder idiom or add volatile.
- Singletons with mutable shared state.
Fix: Make them thread-safe or immutable.
- Calling getInstance() deep in business code.
Fix: Inject the dependency instead.
Interview questions
How do you implement a thread-safe lazy singleton in Java?
Use the initialization-on-demand holder idiom: a private static nested class holds the instance, and the JVM initializes it safely on first access. An enum singleton is another safe option.
Why is Singleton considered an anti-pattern by many?
It introduces global mutable state, hides dependencies, couples code to a concrete class, and makes tests hard to isolate. Dependency injection with a single instance avoids these problems.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Implement a thread-safe lazy singleton three ways | Easy | Concurrency. |
| Refactor a singleton-heavy service to DI | Medium | Testability. |