Creational Pattern

Singleton Pattern

Ensure a class has only one instance and provide a global point of access to it. One of the most commonly asked patterns in interviews.

15 min readHigh Priority

1What is Singleton?

Real-World Analogy
Government of a Country

A country has only ONE government at a time. You cannot create multiple governments. Everyone who needs to interact with the government uses the same instance.

Database Connection Pool

Your application should have only ONE connection pool to manage database connections efficiently. Creating multiple pools would waste resources and cause conflicts.

Singleton is a creational design pattern that ensures a class has only one instance throughout the application lifecycle, and provides a global access point to that instance.
When to Use
  • Database connection pool
  • Logger instance
  • Configuration manager
  • Cache manager
  • Thread pool
When NOT to Use
  • Just for global access (use DI instead)
  • When you need multiple instances later
  • When testability is critical
  • Stateless utility classes

2Implementation Approaches

There are several ways to implement Singleton, each with trade-offs:

1
Eager Initialization
Pros
  • +Thread-safe by default
  • +Simple implementation
Cons
  • -Instance created even if never used
  • -No exception handling in initialization
Best For
Small objects that are always needed
2
Lazy Initialization
Pros
  • +Instance created only when needed
  • +Saves memory if never used
Cons
  • -Not thread-safe without synchronization
Best For
Single-threaded applications
3
Double-Checked Locking (DCL)
Pros
  • +Thread-safe
  • +Lazy initialization
  • +Good performance after first call
Cons
  • -Complex implementation
  • -Requires volatile keyword
Best For
Multi-threaded applications (Java pre-1.5 style)
4
Bill Pugh (Static Inner Class)
Pros
  • +Thread-safe
  • +Lazy
  • +No synchronization overhead
Cons
  • -Java-specific idiom
Best For
Java applications (recommended)

3Implementation

Thread-Safe Singleton Implementation
// ========== Method 1: Eager Initialization ==========
public class EagerSingleton {
    // Instance created at class loading time
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // Private constructor prevents instantiation
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

// ========== Method 2: Double-Checked Locking ==========
public class DCLSingleton {
    // volatile prevents instruction reordering
    private static volatile DCLSingleton instance;
    
    private DCLSingleton() {}
    
    public static DCLSingleton getInstance() {
        if (instance == null) {              // First check (no locking)
            synchronized (DCLSingleton.class) {
                if (instance == null) {      // Second check (with lock)
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

// ========== Method 3: Bill Pugh (Recommended for Java) ==========
public class BillPughSingleton {
    private BillPughSingleton() {}
    
    // Static inner class - loaded only when getInstance() is called
    private static class SingletonHelper {
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }
    
    public static BillPughSingleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

// ========== Method 4: Enum (Most Robust) ==========
public enum EnumSingleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("Singleton using Enum");
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        BillPughSingleton s1 = BillPughSingleton.getInstance();
        BillPughSingleton s2 = BillPughSingleton.getInstance();
        
        System.out.println(s1 == s2);  // true - same instance
    }
}
Output:
Singleton created
true
Singleton doing something

4Thread Safety Deep Dive

Understanding why thread safety matters in Singleton:

The Race Condition Problem

Thread 1                    Thread 2
--------                    --------
if (instance == null)       
                            if (instance == null)
instance = new Singleton()  
                            instance = new Singleton()  // OOPS!
return instance             return instance  // Different instance!

Without proper synchronization, two threads can both see instance == null and create separate instances, breaking the singleton guarantee.

Best Practices by Language
  • Java: Use Bill Pugh (static inner class) or Enum singleton
  • Python: Use module-level instance or metaclass approach
  • C++11+: Use Meyers' Singleton (static local variable)
  • All: Consider dependency injection instead of raw singleton

5Singleton Drawbacks

Singleton is often considered an anti-pattern. Understand why:

Hard to Test
Singletons carry state across tests. You cannot easily mock them or reset them between tests.
Hidden Dependencies
Classes that use singletons have hidden dependencies not visible in their constructors.
Violates Single Responsibility
The class manages both its business logic AND its own lifecycle/instantiation.
Global State
Essentially creates global state, which can lead to unexpected side effects and race conditions.
Modern Alternative: Dependency Injection
Instead of using Singleton directly, use a DI container to manage object lifecycle. The container ensures only one instance exists, while your code remains testable and the dependencies are explicit.

6Interview Follow-up Questions

Interview Follow-up Questions

Common follow-up questions interviewers ask

7Key Takeaways

1Singleton ensures only ONE instance exists with global access.
2Use for: Connection pools, loggers, configuration, caches.
3Thread safety is critical - use proper implementation.
4Java: Bill Pugh or Enum. Python: Module-level. C++: Meyers.
5Consider Dependency Injection as modern alternative.
6Be aware of drawbacks: testing difficulty, hidden dependencies.