← Home
🏗Crack System Design with DSA AnimatorSystem DesignNEW
🏗 System Design›Creational Patterns

🔒Singleton Pattern

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

📌 Key IdeaSingleton restricts instantiation of a class to one object. Powerful for shared resources (config, logger, cache, connection pool) — but easy to misuse and a frequent source of hidden global state in tests.
✨

Why It Matters

  • Resource control — Prevents multiple instances of an expensive object (DB connection pool, thread pool).
  • Consistency — Every caller observes the same state — useful for caches and counters.
  • Lazy creation — Some implementations only create the instance the first time it is requested.
  • Global access — No need to thread a reference through 10 layers of method calls.
💡

In Layman's Terms

Imagine the President of a country: at any moment there is exactly one. Anyone in the country can dial a single hotline ("getInstance()") and reach that one person. You can't spawn a second President with new.

Other examples: the print spooler in your laptop (one queue), the file system in Java (File-system view of the OS), or Spring's ApplicationContext for a given app.

🌍

Real-World Analogy

🖨 Print Spooler Analogy

Operating systems typically have one print spooler — a central queue every program adds documents to. If two spoolers existed, they'd fight over the printer and you'd get garbled pages. The OS therefore makes the spooler a singleton: one instance, accessible globally.

Singletons fit naturally where the *resource itself* is unique (the printer port, the application config file, the in-memory cache).

🖼

Visual

🔒 Single shared instance, many clients
     ┌──────────┐                ┌──────────────────┐
     │ Client A │ ──getInstance─▶ │                  │
     └──────────┘                 │                  │
     ┌──────────┐                 │   ▣  Singleton   │
     │ Client B │ ──getInstance─▶ │   (only ONE)     │
     └──────────┘                 │                  │
     ┌──────────┐                 │                  │
     │ Client C │ ──getInstance─▶ │                  │
     └──────────┘                 └──────────────────┘
                                       ▲
                       all clients receive the
                       same reference (==)
🧱 UML
           ┌────────────────────────────────────┐
           │           Singleton                │
           ├────────────────────────────────────┤
           │ - instance : Singleton  ◀──── static, private
           │ - Singleton()           ◀──── private ctor
           ├────────────────────────────────────┤
           │ + getInstance() : Singleton ◀── only public way in
           │ + doWork()                         │
           └────────────────────────────────────┘

Top — runtime view; many clients all pointing at the one shared instance. Bottom — UML showing the static instance + private constructor.

✅

When to Use

  • Truly shared, expensive resources: connection pools, thread pools, caches.
  • Application-wide configuration loaded once on startup.
  • Logger that funnels output to a single sink (file / stdout).
  • Hardware-coupled state: a printer driver, a sensor proxy.
  • Coordinator objects where having more than one would corrupt invariants (e.g., ID generator, license manager).
🚫

When NOT to Use

  • When you simply want global access — globals != singletons. Pass dependencies in the constructor instead.
  • When you need multiple configurations (test vs prod) — the singleton "happens" to live in static state and breaks isolation.
  • For mutable state that callers will write to from multiple threads — you're inviting race conditions.
  • When you can't tolerate hidden coupling in tests — singletons leak between test cases unless reset.
  • When dependency-injection containers (Spring, Guice, Dagger) are available — let the container manage the lifecycle. You get singleton scope without the pattern's downsides.
🚨

Anti-Pattern (What NOT to Do)

Static getInstance() everywhereBadjava
class ReportService {
    void run() {
        DbPool.getInstance().conn();  // hidden dependency
    }
}
Tests can't substitute a pool; order of init unclear.
✅

The Right Way

Single instance via DI (singleton scope)Goodjava
@Service  // Spring: one bean per context
class ReportService {
    private final DbPool pool;
    ReportService(DbPool pool) { this.pool = pool; }
}
Same uniqueness guarantee in prod without statics — easier tests with @MockBean.
⚖️

Pros & Cons

👍 Pros
  • Single instance guaranteed — no accidental duplicates of expensive resources.
  • Lazy creation possible — pay only when first used (Bill Pugh / DCL variants).
  • Globally reachable without passing references through every layer.
  • Useful for natural singletons: spoolers, OS handles, hardware controllers.
👎 Cons
  • Hidden global state — methods that look pure secretly read/write a singleton.
  • Hard to unit test — tests inherit state from previous tests, must reset manually.
  • Hides dependencies — code that uses X.getInstance() does not advertise that it depends on X.
  • Breaks Single Responsibility — the class manages both its job *and* its lifecycle.
  • Concurrency pitfalls — DCL is famously easy to get wrong without volatile.
  • Subclassing / mocking is awkward — the constructor is private.
📊

Singleton vs. Static Utility vs. Spring Bean

AspectSingletonStatic UtilitySpring `@Component` (singleton scope)
Has stateYes — instance fieldsNo (or static fields)Yes — instance fields
PolymorphismPossible (implements interface)Not reallyYes — DI uses interfaces
Lazy initYes (Bill Pugh / DCL)No — class loadConfigurable per bean
Mockable in testsPainfulPainfulEasy — inject a stub
Lifecycle ownerItselfJVM class loaderSpring container
When to pickResource you control entirelyPure functions / helpersAlmost always, when you have a DI container
🔀

Common Confusions Cleared Up

Singleton vs. Static Class
  • Static classes can't implement interfaces / extend a base — Singletons can. Useful for swapping implementations.
  • Static classes have no instance state — pure functions only. Singletons can hold caches, counters, queues.
  • Static classes can't be passed as a parameter — Singletons can (treat them as objects).
  • Singletons can be lazy; static classes initialize at first reference and can't be deferred.
Singleton vs. Spring Bean (singleton scope)
  • Spring beans are singletons per ApplicationContext, not per JVM. In tests you spin up a fresh context — beans reset automatically.
  • Spring beans are wired by interface and easily mocked. Classic singletons hard-code getInstance() everywhere.
  • Prefer Spring (or any DI) singleton scope; it gives you the same uniqueness guarantee without the testing pain.
Singleton vs. Monostate
  • Monostate hides static fields behind a normal class — every instance shares the same data. Caller uses new Foo() but gets the same state. Less surprising than getInstance() but rarely used in practice.
🧪

Variations / Implementations

1️⃣ Eager initialization

Instance is built once when the class loads. Thread-safe for free, but pays the cost even if you never call it.

Eager Singletonjava
public final class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}
Pick this when the singleton is cheap and almost always used (config, logger).

2️⃣ Lazy initialization (NOT thread-safe)

Builds on first call — but two threads racing can each pass the null check and create two instances.

Lazy Singleton — broken under concurrencyjava
public final class LazyUnsafeSingleton {
    private static LazyUnsafeSingleton instance;
    private LazyUnsafeSingleton() {}

    public static LazyUnsafeSingleton getInstance() {
        if (instance == null) {
            instance = new LazyUnsafeSingleton(); // 🚨 race condition
        }
        return instance;
    }
}
Don't ship this. Shown only to motivate the safe variants below.

3️⃣ Synchronized accessor (thread-safe but slow)

synchronized makes it correct, but every read now pays a lock cost.

Synchronized Singletonjava
public final class SyncSingleton {
    private static SyncSingleton instance;
    private SyncSingleton() {}

    public static synchronized SyncSingleton getInstance() {
        if (instance == null) instance = new SyncSingleton();
        return instance;
    }
}
Correct, but too slow for hot paths.

4️⃣ Double-Checked Locking (DCL) with volatile

Lock only on first creation. volatile is required so other threads never see a half-built instance.

Double-checked Lockingjava
public final class DclSingleton {
    private static volatile DclSingleton instance;
    private DclSingleton() {}

    public static DclSingleton getInstance() {
        DclSingleton local = instance;
        if (local == null) {
            synchronized (DclSingleton.class) {
                local = instance;
                if (local == null) {
                    local = new DclSingleton();
                    instance = local;
                }
            }
        }
        return local;
    }
}
Skip volatile and you have a classic publication bug.

5️⃣ Bill Pugh / Static Holder (recommended for lazy)

The inner class is not loaded until getInstance() is called — lazy + thread-safe with zero locking.

Bill Pugh Singletonjava
public final class HolderSingleton {
    private HolderSingleton() {}

    private static class Holder {
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}
Lazy, thread-safe, simple. Preferred over DCL.

6️⃣ Enum Singleton (Joshua Bloch's recommendation)

A one-element enum is the simplest singleton in Java — and the only one immune to reflection and serialization attacks.

Enum Singletonjava
public enum EnumSingleton {
    INSTANCE;

    private final Map<String, String> cache = new ConcurrentHashMap<>();

    public void put(String k, String v) { cache.put(k, v); }
    public String get(String k)         { return cache.get(k); }
}

// Usage:  EnumSingleton.INSTANCE.put("env", "prod");
“A single-element enum type is the best way to implement a singleton.” — *Effective Java*, Item 3.
⚠️

Common Mistakes / Anti-Patterns

  • Using Singleton as a global variable bucket — drop in unrelated state because "it's convenient".
  • Mutable singletons accessed from many threads without synchronization → race conditions.
  • Singletons that depend on other singletons → tangled init order, classic NPE on startup.
  • Forgetting volatile in DCL — one of the most famous broken patterns in Java history (the "DCL is broken" papers).
  • Allowing reflection to break it: setAccessible(true) on the private constructor → second instance.
  • Forgetting to override readResolve() on a Serializable singleton → deserialization creates a new instance.
🎤

Interview Q&A

🔮

Likely Follow-up Questions

  • Implement a thread-safe Singleton on the whiteboard. (Holder idiom or enum is the safest answer.)
  • How would you write a Singleton that supports two configurations (e.g., test vs prod)? (You probably shouldn't — switch to DI.)
  • How does Spring achieve singleton scope? (Bean factory caches instances per ApplicationContext.)
  • Walk me through why the original double-checked locking was broken before Java 5. (Memory model didn't guarantee a happens-before relationship; volatile semantics changed in JSR-133.)
  • How would you make a Singleton lazily initialized and parameterized? (Holder idiom + builder, or "initialize-on-first-use" with explicit init(config) — usually a smell.)
  • Name three real-world Singletons in the JDK or popular frameworks. (Runtime.getRuntime(), Logger.getLogger("name") per name, Spring's default bean scope.)
🧾

Quick Cheat Sheet

TypeCreational design pattern (GoF).
GoalExactly one instance + single access point.
Best in JavaEnum (Bloch) or Bill Pugh / Holder idiom.
AvoidNaive lazy init, broken DCL without volatile, hidden global state.
SmellsUsed as a junk drawer for unrelated globals; calling getInstance() from deep in domain logic.
Modern advicePrefer DI container singleton scope (Spring/Guice) over hand-rolled Singletons.
🔗

Related Topics