DevNotes
Support
Essential Design Patterns · Chapter 03

Singleton Pattern

Guaranteeing one instance — the enum singleton, eager and lazy initialization, double-checked locking with volatile, the holder idiom, breaking singletons with reflection and serialization, and Spring singleton scope.

◷ 6 min read§ 13 sections? 6 interview questions{ } 8 code examples

#Introduction

Purpose: ensure only one instance of a class exists, and provide a global point of access to it.

Typical uses: configuration holders, registries, caches, connection managers and loggers — objects where several copies would waste resources or cause inconsistency.

The essentials of every implementation:

private constructor
single static instance
global access method

#Eager Initialization

Java
public class AppConfig {
    private static final AppConfig INSTANCE = new AppConfig();   // created when the class loads

    private AppConfig() { }

    public static AppConfig getInstance() {
        return INSTANCE;
    }
}

Simple and thread-safe (class initialization is thread-safe), but the instance is created even if never used.

#Lazy Initialization — the Broken Version

Java
public class AppConfig {
    private static AppConfig instance;

    private AppConfig() { }

    public static AppConfig getInstance() {
        if (instance == null) {             // ❌ two threads can both see null
            instance = new AppConfig();     //    and create two instances
        }
        return instance;
    }
}

Fine in single-threaded code, broken under concurrency. Making the whole method synchronized fixes it but locks on every call.

#Double-Checked Locking

Java
public class Singleton {

    private static volatile Singleton instance;

    private Singleton() { }

    public static Singleton getInstance() {
        if (instance == null) {                      // 1st check — no locking once created
            synchronized (Singleton.class) {
                if (instance == null) {              // 2nd check — inside the lock
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
PartWhy it's there
First ifSkips the lock on every call after creation — fast path
synchronizedOnly one thread creates the instance
Second ifAnother thread may have created it while we waited for the lock
volatilePrevents a thread from seeing a reference to a partially constructed object

#Bill Pugh / Holder Idiom

Java
public class Singleton {

    private Singleton() { }

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

    public static Singleton getInstance() {
        return Holder.INSTANCE;   // Holder is loaded — and INSTANCE created — on first call
    }
}

Lazy, thread-safe and lock-free, relying on the JVM's guarantee that class initialization happens once. It's usually preferable to double-checked locking.

#Comparison

ImplementationLazyThread-safeSerialization-safeReflection-safe
EnumOn first use of the enum✅✅✅
Eager static field❌✅Needs readResolve❌
Unsynchronized lazy✅❌❌❌
synchronized method✅✅ (slow)Needs readResolve❌
Double-checked locking✅✅ (with volatile)Needs readResolve❌
Holder idiom✅✅Needs readResolve❌

#Breaking a Singleton

Reflection

Java
Constructor<Singleton> c = Singleton.class.getDeclaredConstructor();
c.setAccessible(true);
Singleton second = c.newInstance();   // a second instance!

Defence: throw from the constructor if an instance already exists — or use an enum.

Serialization

Deserializing a Serializable singleton creates a new object. Defence:

Java
protected Object readResolve() {
    return getInstance();   // replace the deserialized copy with the real instance
}

Cloning and Multiple Class Loaders

Don't implement Cloneable (or throw CloneNotSupportedException). And note that a "singleton" is one instance per class loader — application servers with several class loaders can hold several copies.

#Singleton in Spring

Spring beans are singleton-scoped by default — one instance per application context, not per JVM, and without a private constructor:

Java
@Service
public class PricingService { ... }   // one shared instance, created and managed by Spring
GoF SingletonSpring singleton bean
Enforced by the class itselfManaged by the container
One per class loaderOne per ApplicationContext
Global static access (getInstance())Obtained by dependency injection
Hard to mock and testEasy to replace in tests

In Spring applications, prefer singleton beans over hand-written singletons — and keep them stateless, because every request thread shares them.

#Drawbacks

  • Global state — hidden dependencies make code harder to reason about.
  • Testing — static getInstance() calls are hard to mock.
  • Concurrency — a mutable singleton is shared by all threads and must be thread-safe.
  • Often considered an anti-pattern when overused; dependency injection solves most of the same problems more cleanly.

#Interview Questions

Q1What is the Singleton pattern?

A creational pattern that ensures a class has only one instance and provides controlled access to it.

Q2How do you make a singleton thread-safe?

Use an enum, eager initialization, the holder idiom, or double-checked locking with a volatile field.

Q3Why is volatile needed in double-checked locking?

Because object construction and reference assignment can be reordered; without volatile, another thread may see a non-null reference to an object whose constructor hasn't finished.

Q4Why is the enum singleton considered the best approach?

The JVM guarantees one instance, it's thread-safe, and it's protected against both serialization and reflection attacks with no extra code.

Q5How can a singleton be broken, and how do you prevent it?

Reflection (call the private constructor) — guard the constructor or use an enum. Serialization — implement readResolve(). Cloning — don't support clone(). Multiple class loaders — each loads its own copy.

Q6Is a Spring singleton bean the same as the Singleton pattern?

No. A Spring singleton is one instance per application context, managed and injected by the container; the GoF singleton enforces one instance per class loader through the class itself.

#Common Mistakes

  • Lazy initialization without synchronization.
  • Double-checked locking without volatile.
  • Mutable state in a singleton without thread safety.
  • Forgetting readResolve() on a serializable singleton.
  • Using singletons as global variables, hiding dependencies.

#Key Takeaways

  • Private constructor + single static instance + global access method.
  • The enum singleton is the simplest robust implementation.
  • Double-checked locking requires volatile; the holder idiom is simpler.
  • Reflection, serialization and cloning can break non-enum singletons.
  • In Spring, prefer container-managed singleton beans — and keep them stateless.