CREATIONAL PATTERNS / OBJECT DESIGN BRIEF

Singleton pattern

The Singleton pattern ensures a class has exactly one instance and provides global access to it.

BeginnerPhase 04 / Topic 5 of 6ResponsibilitiesCollaborationsExtensibility
01

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().

A country's president

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.

02

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.
03

Where it shows up in interviews

Thread-safe initialization

Recognize it when: 'implement a singleton' or shared manager in LLD.

  • Design a logger
  • Design a parking lot manager
  • Design a configuration service
04

Where it is used in real software

Java Runtime.getRuntime()

The JDK exposes one Runtime per JVM.

Spring beans

Default singleton scope: one instance per container, injected where needed, without static access.

Node.js module cache

A module's exports are created once and shared by every importer.

05

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.
06

How it works, step by step

  1. 1
    Ask if one instance is truly required

    Often DI scope is better.

  2. 2
    Make the constructor private

    No outside new.

  3. 3
    Provide a static accessor

    getInstance().

  4. 4
    Make initialization thread-safe

    Holder idiom, enum, or eager init.

  5. 5
    Keep it testable

    Depend on an interface; allow injection in tests.

07

Java singleton implementations

Thread safety and laziness

Step 1 / 5
ApproachLazy?Thread-safe?Notes
Eager static fieldNoYesSimplest
Synchronized getInstanceYesYesLock on every call
Double-checked locking + volatileYesYesError-prone if volatile is missing
Holder idiomYesYesJVM class loading guarantees safety
EnumNoYesAlso 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.

08

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(); }}
09

Complexity and performance

AccessO(1)

Static field.

Holder idiom lock costNone after init

JVM guarantees.

10

Trade-offs

Global state

Hidden dependencies make code harder to reason about and test.

One per what?

A static singleton is one per classloader or process, not per cluster; distributed systems need other coordination.

11

Variants and related techniques

DI singleton scope

Container creates one instance and injects it.

Multiton

One instance per key (per tenant or region).

12

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.

13

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.

14

Practice problems

ProblemDifficultyWhat it trains
Implement a thread-safe lazy singleton three waysEasyConcurrency.
Refactor a singleton-heavy service to DIMediumTestability.