Singleton Pattern: Java, Python & TypeScript Examples
Ensure a class has only one instance and provide global access to it — the creational design pattern for controlled object creation, with production-ready implementations.
Overview
Your app just opened a second database connection pool and now every query competes for half the connections. That’s the problem Singleton exists to solve: some resources must exist exactly once — a connection pool, a config reader, a logger — and the Singleton Pattern guarantees that single instance plus a controlled way to reach it.
The pattern comes from the Gang of Four (Design Patterns, 1994), and it’s the most widely used — and most abused — creational pattern. This page covers the implementations that actually survive production: lazy and eager init, double-checked locking with volatile, the Bill Pugh holder, Java’s enum singleton, module-level singletons in Python and Node, registries, and the dependency-injection alternative you’ll want for anything testable.
When to Use
Use Singleton when all of these hold:
- Exactly one instance must exist — a connection pool, an in-memory cache, a config manager, a logger factory.
- A second instance breaks things — a second pool doubles the connections hitting your database.
- You want lazy initialization to defer expensive setup until first use.
Do not use it when:
- The object is mutable business state — that’s global state with extra steps. Pass it as a parameter instead.
- You need per-user or per-request data — a singleton holding request state is a memory leak.
- Testability matters more than convenience — prefer dependency injection, which gives you the same single instance with an explicit contract.
Solution
Java — the implementations that matter
Start with the enum singleton. It’s three lines, it’s thread-safe by JVM guarantee, and it’s the only variant immune to serialization and reflection attacks (Joshua Bloch’s recommendation in Effective Java, item 3):
public enum Configuration {
INSTANCE;
private final Map<String, String> properties = loadProperties();
public String get(String key) {
return properties.get(key);
}
private Map<String, String> loadProperties() {
return Map.of("env", System.getenv("APP_ENV"));
}
}
// Usage — the same instance everywhere, impossible to duplicate
String env = Configuration.INSTANCE.get("env");
If you can’t use an enum (you need inheritance), the Bill Pugh holder idiom gives lazy init with zero synchronization cost — the JVM only loads the inner class on first access:
public class ConnectionPool {
private ConnectionPool() {
// expensive setup: open pool, bind sockets
}
private static class Holder {
static final ConnectionPool INSTANCE = new ConnectionPool();
}
public static ConnectionPool getInstance() {
return Holder.INSTANCE;
}
}
And if you need conditional or configurable construction, double-checked locking works — but only with volatile, otherwise a second thread can read a half-constructed instance:
public class CacheClient {
private static volatile CacheClient instance;
private CacheClient() {}
public static CacheClient getInstance() {
if (instance == null) { // fast path, no lock
synchronized (CacheClient.class) {
if (instance == null) { // second check inside the lock
instance = new CacheClient();
}
}
}
return instance;
}
}
The naive synchronized getInstance() you see in older tutorials is correct but slower — it locks on every call, not just the first.
Python — prefer the module, not __new__
The classic __new__ singleton has two traps nobody warns you about: __init__ runs again on every call (re-initializing your state), and the check-then-act inside __new__ is not atomic — the GIL doesn’t save you here, two threads can still race between the None check and the assignment. If you insist on __new__, guard it with a lock:
import threading
class ConnectionPool:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return # don't re-run setup
self._pool = self._create_pool()
self._initialized = True
The cleaner Python idiom is the module itself: modules are imported once and cached in sys.modules, so a module-level instance is a singleton for free — thread-safe, lazy on first import, and the recommended approach:
# connection_pool.py
import psycopg2.pool
pool = psycopg2.pool.ThreadedConnectionPool(
minconn=2, maxconn=10,
dsn="postgresql://user:pass@localhost/db",
)
# Every importer gets the same object:
from connection_pool import pool
TypeScript — private constructors and registries
A private constructor blocks new, and a static accessor controls the single instance. For anything beyond the simplest case, a named registry is more useful — it maps keys to instances, so you get “one per name” (a Multiton actually) plus a reset hook for tests:
class Registry {
private static instances = new Map<string, unknown>();
static get<T>(key: string, factory: () => T): T {
if (!Registry.instances.has(key)) {
Registry.instances.set(key, factory());
}
return Registry.instances.get(key) as T;
}
static reset(key: string): void {
Registry.instances.delete(key);
}
}
const pool = Registry.get('db-pool', () => createPool(config));
Registry.reset('db-pool'); // in tests, between cases
Node.js also gives you singletons for free the same way Python does: require/import caches modules, so an exported instance is shared by every importer.
Diagram
Explanation
Every Singleton implementation answers the same three questions:
- Who blocks
new? A private constructor (Java/TS), an enum, or the language’s module system (Python/Node). - When is the instance created? Eagerly at load, or lazily on first access — lazy saves startup cost but introduces the thread-safety problem.
- Who enforces single-instance? The class itself, a registry, or the DI container.
On thread safety: the race is always in lazy init. Two threads hit if (instance == null) at once, both construct, and the second overwrites the first. synchronized fixes it naively; volatile + double-checked locking keeps the fast path lock-free; the holder idiom offloads it to class-loading guarantees. In Java there’s a subtler issue — without volatile, a thread can see a non-null reference to an object whose constructor hasn’t finished writing fields, because the JVM may reorder the “allocate → construct → assign” steps.
Variants
| Variant | Lazy | Thread-safe | Testable | When it wins |
|---|---|---|---|---|
| Eager static | No | Yes | Poor | Cheap init, always needed |
| Synchronized accessor | Yes | Yes | Poor | Simple, low-traffic |
| Double-checked locking | Yes | Yes* | Poor | Hot path, Java/C# — needs volatile |
| Bill Pugh holder | Yes | Yes | Poor | Java lazy init, no lock cost |
| Enum singleton | No | Yes | Poor | Java — survives serialization and reflection |
| Module-level | Yes | Yes | Poor | Python, Node — idiomatic, zero machinery |
| Registry / Multiton | Yes | Yes | Good | Named instances, test reset hooks |
| DI container | Yes | Yes | Excellent | Anything you need to mock |
*Only with volatile; without it the check-then-act is still broken.
Singleton in tests — the part tutorials skip
A getInstance() singleton makes tests order-dependent: whatever one test mutates leaks into the next. Three escapes:
// 1. Registry with reset — clear between tests
afterEach(() => Registry.reset('db-pool'));
// 2. DI container — bind a mock for the test run
container.bind(IPool).to(MockPool).inSingletonScope();
const service = container.get(OrderService);
// 3. Constructor injection — pass the instance explicitly
const service = new OrderService(pool); // pool is the singleton in prod, a fake in tests
The honest answer is option 3. If a piece of code might ever need a second implementation in tests, inject the dependency and keep the singleton only as the production binding.
Serialization, reflection, and classloaders
Three ways Singleton breaks silently, all real:
- Serialization (Java): deserializing creates a new instance regardless of the private constructor. Fix it with
readResolve():
Or use an enum singleton, which handles it automatically.protected Object readResolve() { return getInstance(); } - Reflection:
setAccessible(true)can call your private constructor. Enum singletons are immune — the JVM guarantees a single constant. - Multiple classloaders: two classloaders loading the same class produce two “singletons” — common in app servers and plugins. Python and Node do the same with duplicate package copies. If you’re seeing two instances, look there first —
System.identityHashCode()orid()will confirm it.
Alternatives
| Option | What it gives you | What it costs |
|---|---|---|
| DI container (Spring, ASP.NET, Nest) | Singleton lifecycle + testability + explicit contracts | Framework dependency |
| Module system (Python, Node) | Idiomatic singleton for free | Hard to swap in tests |
| Monostate | Shared state, multiple instances | Same global-state risks |
| Static class | Simplest possible | No polymorphism, no mocking, no injection |
Singleton vs static class deserves the extra line: a singleton is an object — it can implement an interface, be passed as a parameter, and be replaced in tests. A static class can’t do any of that.
What Works
- Keep the singleton’s state immutable or read-only after init — mutable global state is where the real bugs live.
- For Java: reach for enum or Bill Pugh first; they’re correct without you proving it.
- For Python/Node: use the module system; don’t reinvent
getInstance(). - Put the “how do I test this” story in writing — a registry
reset()or a DI binding — before the first teammate hits it. - Document what the singleton owns (connections, cache) and what cleanup looks like at shutdown.
Common Mistakes
- Assuming
__new__is thread-safe because of the GIL — it isn’t; use a lock or module-level init. - Forgetting
__init__re-runs on everySingleton()call in Python and re-initializes the state. - Double-checked locking without
volatile— looks right, races in production. - A singleton holding per-request or per-user data — that’s a memory leak wearing a pattern’s clothes.
- Business-logic services as singletons — a
UserServicesingleton is a coupling bomb; inject it instead. - Skipping the shutdown path — pools and executors leak if nobody calls
close()on exit.
Further Reading
- Companion resources — the runnable Python, Java, and TypeScript implementations from this page.
- Singleton — refactoring.guru — canonical GoF explanation with UML.
- Effective Java — Joshua Bloch — item 3 on the enum singleton.
- Singletons are Pathological Liars — Google Testing Blog — the testability argument.
synchronizedand the Java Memory Model — JLS §17 — whyvolatileis mandatory in DCL.
Frequently Asked Questions
Is the Singleton Pattern an anti-pattern?
Singleton itself isn't the problem — global mutable state is. A singleton for an immutable config or a connection pool is legitimate infrastructure. A singleton for business state makes tests order-dependent and couples everything to everything.
How do I make a Python singleton thread-safe?
Don't rely on the GIL — the check-then-act inside __new__ can still race. Guard __new__ with threading.Lock, or better, use a module-level instance: Python modules are imported once and cached, so they're thread-safe singletons by construction.
What's the best singleton implementation in Java?
The enum singleton. It's thread-safe by JVM guarantee, lazy at class init, and immune to serialization and reflection attacks — no other variant is. If you need lazy init with a real class, use the Bill Pugh holder idiom.
How do I test code that depends on a singleton?
Best answer: don't call getInstance() inside business logic — inject the dependency through the constructor and bind the singleton in the DI container. If you're stuck with getInstance(), use a registry variant with a reset() hook so each test gets a fresh instance.
Singleton vs static class — when does it matter?
A singleton is an object: it can implement interfaces, be injected, and be mocked. A static class is just a namespace for functions. If you might need polymorphism or test doubles, use a singleton; if it's pure stateless utility, a static class is simpler.
Does double-checked locking still need volatile?
Yes, in Java. Without volatile a thread can observe a non-null reference to a partially constructed object — the JVM is allowed to publish the reference before the constructor finishes. It's the most commonly broken implementation detail.
Can a singleton be serialized?
Naively, yes — and deserialization will create a second instance. Implement readResolve() to return the existing instance, or use an enum singleton, which handles it automatically.
What's the difference between Singleton and Multiton?
A Singleton enforces exactly one instance; a Multiton enforces one instance per key — a map of named singletons. Registries are Multitons in practice.
Related Resources
Factory Pattern
Create objects without specifying the exact class to instantiate. A creational design pattern for flexible object creation.
PatternMultiton Pattern
Manage a map of named singleton instances, providing controlled access to a finite set of shared objects identified by keys.
PatternRegistry Pattern
Centralize access to shared services and objects via a lookup table. A structural pattern that decouples consumers from concrete implementations.
PatternDependency Injection Container in TypeScript
Build a lightweight DI container that resolves class dependencies automatically, enabling testable, loosely-coupled applications without frameworks like Angular or InversifyJS
PatternObject Pool Pattern
Reuse expensive objects instead of creating and destroying them repeatedly. A creational pattern for managing scarce resources efficiently.
GuidePractical Design Patterns Guide
Pick the right design pattern for each problem: Singleton, Factory, Observer, Strategy and more, with runnable code and honest trade-offs.