Creational Pattern

Singleton

Ensure a class has only one instance and provide a global point of access to it.

Exactly one instance.

The Singleton pattern guarantees that a class has just one instance in the whole program and gives everyone a single, shared way to reach it. Think of a single settings object or a single connection to a printer spooler.

You make the constructor private so no one can create new instances, keep the one instance in a static field, and expose a static getInstance() method that returns it.

bad.py
# Problem: "singleton" via a global — hard to test, hidden dependency.
_db = None
def get_db():
    global _db
    if _db is None:
        _db = Database("prod")    # created on first use, forever
    return _db

def save_user(u):
    get_db().insert(u)            # callers don't know they share state
good.py
# Fix: prefer explicit injection. If you must singleton, make it obvious.
class Database:
    def __init__(self, url): self.url = url
    def insert(self, row): ...

# App startup creates ONE instance and passes it in.
db = Database("prod")

class UserService:
    def __init__(self, database: Database):
        self.database = database  # no hidden global

    def save(self, user):
        self.database.insert(user)

# Tests pass a fake Database — no global to reset.

Eager vs lazy, thread safety.

Two common flavors: eager (create the instance at class load — simple and thread-safe) and lazy (create on first use — saves resources but needs care under concurrency). A naive lazy singleton has a race: two threads can both see null and create two instances.

  • Double-checked locking — check, lock, check again; the field must be volatile.
  • Initialization-on-demand holder — a static inner class loaded lazily by the JVM, thread-safe without explicit locks.
  • Enum singleton (Java) — concise, serialization- and reflection-safe.

Why it's often an anti-pattern.

  • Global state — singletons are hidden global variables; they couple code and make reasoning about state hard.
  • Testability — hard to mock/replace; tests leak state between each other.
  • Hidden dependencies — callers reach out to getInstance() instead of declaring the dependency; DI is usually better.
  • Serialization & reflection can break the single-instance guarantee unless defended (e.g. readResolve, enum).
  • Preferred alternative — register a single instance in a DI container and inject it (single instance without the global access point).

Interview Questions

Filter

A creational pattern that ensures a class has only one instance and provides a single global access point to that instance.

Creational — it deals with object creation, specifically constraining how many instances can exist.

Make the constructor private (or protected), so instances can only be created inside the class itself, and expose a static accessor that returns the single instance.

Logging facilities, configuration/settings objects, a connection or thread pool manager, caches, device drivers, or a registry — anywhere a single shared coordinating object makes sense.

Creating the single instance only when it is first requested (on the first call to getInstance()), rather than when the class is loaded, so you don't pay the cost until it's needed.

Creating the instance up front — typically in a static initializer when the class loads. It's simple and inherently thread-safe, at the cost of creating the object even if it's never used.

It introduces global state and hidden dependencies, which increase coupling and make code harder to test and reason about — many consider it an anti-pattern when overused.

A Singleton is a real object instance (it can implement interfaces, be passed around, hold state, and be lazily created), while a static class is just a container of static members. Singletons support polymorphism and can be injected; static classes cannot.

It can. Because it's a single shared object, any state it holds is effectively global and shared by all callers — which is exactly why shared mutable state in a singleton needs careful handling.

Two threads can call getInstance() simultaneously, both find the instance is null, and both create a new instance — resulting in two objects and violating the singleton guarantee (and possibly returning different instances to different callers). The check-then-create sequence isn't atomic.

You first check if the instance is null without locking (fast path); if it is, you acquire a lock and check again inside the lock before creating it. The outer check avoids locking on every call once initialized; the inner check prevents two threads that both passed the first check from creating two instances. The instance field must be volatile to prevent seeing a partially-constructed object due to instruction reordering.

Without volatile, the write that publishes the new instance can be reordered so the reference becomes visible before the object's constructor finishes. Another thread could then read a non-null but partially-initialized object. volatile establishes a happens-before relationship and forbids that reordering, ensuring a fully-constructed instance is seen.

A thread-safe lazy singleton (Java) that puts the instance in a static field of a private static nested class. The nested class isn't loaded until it's first referenced (inside getInstance()), and the JVM guarantees class initialization is thread-safe. So you get lazy loading and thread safety with no synchronization overhead and no volatile/locking code.

A single-element enum is concise, inherently thread-safe on initialization, and — crucially — the JVM guarantees only one instance even under serialization and reflection, which normal class-based singletons must defend against manually. Joshua Bloch recommends it as the preferred approach, though it's less flexible (can't lazy-init easily or extend a class).

Deserializing a serialized singleton creates a brand-new instance, so you can end up with more than one. To defend it, implement readResolve() to return the existing instance during deserialization (and be careful with all fields being transient). Enum singletons avoid this problem entirely.

Reflection can access and invoke the private constructor (e.g. setAccessible(true)), creating additional instances. You can guard against it by throwing an exception in the constructor if the instance already exists, but this is fragile — enum singletons are immune since the JVM forbids reflective enum construction.

Its global access point means classes reach for it directly instead of receiving it as a dependency, so you can't easily substitute a test double. The single shared instance also persists across tests, so state set in one test leaks into another, causing order-dependent, flaky tests. Resetting or replacing the instance requires hacks unless the design allows injection.

DI containers can manage a single instance ("singleton scope") and inject it wherever needed. This gives you the "one instance" benefit without the classic Singleton's global access point and hard-coded coupling: consumers declare the dependency in their constructor and receive the shared instance, so it's mockable and explicit. It's generally the preferred way to achieve "one shared instance" in modern code.

No. Only certain implementations (eager initialization, static holder idiom, enum, or properly synchronized lazy init) are thread-safe. A plain lazy if (instance == null) is not. Separately, even a safely-created singleton isn't automatically safe for concurrent use — its own methods/state need their own synchronization if accessed by multiple threads.

It bundles two responsibilities (managing its single-instance lifecycle and doing its actual job — an SRP violation) and, more importantly, it's global mutable state in disguise. That creates tight, hidden coupling (any code can reach it without declaring the dependency), non-determinism and test pollution from shared state, difficulty parallelizing tests, and problems in multi-classloader/multi-tenant environments where "one instance" is ambiguous. The single-instance requirement is often a valid need, but enforcing it via a global accessor is the part that's harmful; injecting a single instance achieves the goal without the downsides. So it's less that "one instance" is bad and more that the classic implementation encourages global-state coupling.

"One instance per JVM" is really "one instance per class object," and a class loaded by two different classloaders is two distinct classes, each with its own static instance — so you can get multiple singletons in the same JVM (common in app servers, OSGi, plugin systems). To have a truly single instance you'd need to load the class from a shared parent classloader or store the instance in a well-known shared location. This subtlety often surprises people and shows the "global" guarantee is scoped to the classloader, not the process.

A synchronized getInstance() is correct but locks on every call, adding contention even after initialization. Double-checked locking avoids locking on the common path but requires a volatile field and is easy to get subtly wrong (it was actually broken in Java before the JMM was fixed in Java 5). The initialization-on-demand holder idiom achieves lazy, thread-safe initialization with no synchronization or volatile at all, relying on the JVM's guaranteed-safe, lazy class initialization — so it's usually the cleanest choice for a lazy singleton in Java. For most cases, though, enum (eager) or the holder idiom (lazy) beat hand-rolled DCL.

For a class-based singleton: implement readResolve() to return the canonical instance (defeating serialization duplication) and make fields transient; throw from the constructor if an instance already exists (partial defense against reflection, though reflection can still bypass this by other means); and override clone() to throw CloneNotSupportedException (or don't implement Cloneable). Even with all this, reflection can be circumvented, so the robust answer is to use an enum singleton, which the JVM guarantees to be a single instance across serialization and reflective attacks, eliminating all three attack vectors in one stroke. This is precisely why Bloch recommends the enum approach when the guarantee must be ironclad.

When the constraint of exactly one instance is a real domain/technical invariant — e.g. a single hardware resource (a physical device, a single audio mixer), a process-wide coordinating registry, or an immutable/stateless shared object where global state concerns don't apply (stateless singletons are far less problematic). Even then, prefer expressing it via a DI container's singleton scope and injecting it, so you keep the one-instance benefit while retaining testability and explicit dependencies. The key discriminator: is "only one" a genuine requirement, and is the object stateless or its state truly meant to be global? If yes, singleton (ideally injected) is fine; if it's just convenient global access to a service, that's the smell.

Introduce dependency injection incrementally: change classes to accept the dependency via their constructor instead of calling Singleton.getInstance() internally, then wire the single instance in a composition root or DI container with singleton scope. Extract an interface for the singleton so consumers depend on an abstraction (enabling mocking and swapping). Where a singleton mixed lifecycle with behavior, split those responsibilities. Do it behind tests, one consumer at a time (strangler-style), so behavior is preserved while hidden global dependencies become explicit, injected, and testable. The end state keeps a single shared instance managed by the container but removes the global static accessor and its coupling.

Monostate (a.k.a. Borg in Python) achieves "shared state" differently: it lets you create many instances freely, but all instances share the same state by storing their attributes in shared/class-level storage. So behavior is singleton-like (one logical state) while syntax is normal (you just new objects as usual, transparently). Advantages: it's transparent to callers (no special accessor), works with normal construction, and can be more subclass-friendly. Disadvantages: it's still global state (same testability concerns), can be surprising (two "different" objects mysteriously share state), and doesn't restrict instance count. Singleton controls instance identity (one object); Monostate controls state (many objects, one state). Both are ways to model "one" and share the same fundamental global-state caveats.