Singleton guarantees a class has exactly one instance and provides a global access point to it. It's perfect for things that genuinely should be one — config registries, database connection pools, logger sinks. It's a disaster when used as a convenient global — it tightly couples every caller, kills testability, and silently breaks under concurrency or in distributed deployments.
Use Singleton when a single instance is a *correctness* requirement (one connection pool, one configuration, one cache shared per process). Avoid it as a "convenience global" — if multiple instances would still work, you don't need Singleton, you need dependency injection.
Try it
When Singleton is the right call
- Connection pools — opening a new database connection per call would exhaust the database.
- Configuration — every component reads the same config; mutating it from two different copies would be a nightmare.
- Shared in-memory cache — by definition there’s one cache per process.
- Logger — appending to a single sink, often with a buffered writer.
When Singleton is the wrong call
- As a global parameter passer. “I’ll just stash the current user in a singleton” → now every method secretly depends on it, you can’t test, and concurrent requests overwrite each other.
- For business logic. A
OrderServicesingleton is just a static class with extra steps. Use DI. - When you might want two for testing or multi-tenancy. Hard-coded singletons can’t be partitioned.
Implementation pitfalls
Thread safety — naive lazy init is racy. Use one of:
- Eager init:
private static final Foo INSTANCE = new Foo(); - Holder idiom (JVM): nested class loaded only on first access.
- Double-checked locking with
volatile. - Language built-ins: Kotlin
object, Python module-level constants.
Subclasses & serialization — both can break the “one instance” guarantee unless you explicitly handle them.
The system-design connection
In a single-process app, Singleton enforces “one shared resource.”
Comments 0
Discuss this page. Markdown supported. Be kind.