Question 3 · Section 2

What is Singleton?

Singleton can implement an interface (testability via mocks), static class — cannot. Singleton supports lazy initialization, static class initializes on class loading.

Language versions: English Russian Ukrainian

Junior Level

Singleton is a pattern that guarantees a class will have only one instance.

Why: When a resource is shared (log file, DB connection, configuration) and multiple copies would lead to conflicts or inconsistent state.

Simple analogy: A country can have only one president. If you ask “who is the president?”, you’ll always get the same person.

How it works:

  1. Private constructor (cannot create via new)
  2. Static field with the single instance
  3. Static method to get that instance

Example:

public class Logger {
    // 1. The single instance
    private static Logger instance = new Logger();

    // 2. Private constructor
    private Logger() {}

    // 3. Method to get the instance
    public static Logger getInstance() {
        return instance;
    }

    public void log(String message) {
        System.out.println(message);
    }
}

// Usage — always the same object
Logger logger1 = Logger.getInstance();
Logger logger2 = Logger.getInstance();
System.out.println(logger1 == logger2); // true (same object!)

When to use:

  • Logger (one for the entire application)
  • Configuration (one settings file)
  • DB connection (connection pool)

Singleton vs static class

Singleton can implement an interface (testability via mocks), static class — cannot. Singleton supports lazy initialization, static class initializes on class loading.


Middle Level

Singleton Implementations

1. Early Initialization (simple):

public class Singleton {
    private static Singleton instance = new Singleton(); // On class loading
    private Singleton() {}
    public static Singleton getInstance() { return instance; }
}
// Thread-safe
// Not lazy (created even if not needed)

2. Lazy Initialization:

public class Singleton {
    private static Singleton instance;
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
// Lazy
// Not thread-safe!

3. Thread-Safe (synchronized):

public class Singleton {
    private static Singleton instance;
    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
// Thread-safe
// Slow: synchronized adds ~10-50ns per call, critical in hot paths with millions of calls.

4. Enum Singleton (best):

public enum Singleton {
    INSTANCE;

    public void doWork() {
        System.out.println("Working...");
    }
}

// Usage
Singleton.INSTANCE.doWork();
// Thread-safe
// Protected from reflection
// Protected from serialization

How to “break” Singleton

1. Reflection:

// Ordinary Singleton can be broken
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton fake = constructor.newInstance();  // Second instance!

// Enum is protected
Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor();
// → IllegalArgumentException: Cannot reflectively create enum objects

2. Serialization:

// On deserialization a new object is created
ObjectOutputStream out = ...;
out.writeObject(Singleton.getInstance());

ObjectInputStream in = ...;
Singleton another = (Singleton) in.readObject();  // New object!

// Solution: readResolve() method
private Object readResolve() {
    return getInstance();  // Return existing instance
}

3. ClassLoader:

// Multiple ClassLoaders → multiple Singletons!
// One in Tomcat, another in OSGi

Singleton in Spring

// Spring manages Singleton itself
@Component  // Singleton scope by default!
public class UserService { }

// Always the same bean
@Autowired UserService service1;
@Autowired UserService service2;
// service1 == service2 → true

Typical Mistakes

  1. Singleton instead of DI
    // Hidden dependency
    public class OrderService {
        public void create() {
            Logger.getInstance().log("...");  // Hidden dependency!
        }
    }
    
    // DI approach
    public class OrderService {
        private final Logger logger;
        public OrderService(Logger logger) { this.logger = logger; }
    }
    
  2. Mutable state in Singleton
    // Mutable Singleton = concurrency problems
    public class Counter {
        private int count = 0;  // Race condition!
        public void increment() { count++; }
    }
    

Senior Level

Contextuality of Singleton

“Single instance” is relative:

Context Uniqueness
JVM One instance per JVM
ClassLoader One per ClassLoader (can be multiple!)
Spring Context One per ApplicationContext
Distributed system DOES NOT EXIST (needs distributed lock)

In a cluster:

Node 1: Singleton instance A
Node 2: Singleton instance B
Node 3: Singleton instance C
→ 3 instances! Need Redis/Zookeeper for coordination

Java Memory Model and Singleton

JMM (Java Memory Model) — specification defining how threads see each other’s memory. Bill Pugh Singleton — named after the researcher who proposed the static inner class solution. SRP (Single Responsibility Principle).

Visibility problem without volatile:

// Unsafe code
public class UnsafeSingleton {
    private static UnsafeSingleton instance;

    public static UnsafeSingleton getInstance() {
        if (instance == null) {
            instance = new UnsafeSingleton();
        }
        return instance;
    }
}

// Thread 1: allocated memory → published reference → called constructor
// Thread 2: sees instance != null → gets UNINITIALIZED object!

// Solution: volatile + double-checked locking
private static volatile UnsafeSingleton instance;

public static UnsafeSingleton getInstance() {
    UnsafeSingleton local = instance;
    if (local == null) {
        synchronized (UnsafeSingleton.class) {
            local = instance;
            if (local == null) {
                instance = local = new UnsafeSingleton();
            }
        }
    }
    return local;
}

Why volatile is critical:

  • Without volatile: instruction reorder (allocate memory → publish → constructor)
  • With volatile: happens-before guarantee → constructor COMPLETED before publishing

Singleton as Anti-pattern

Problems:

  1. Tight Coupling
    // Dependency not visible in signature
    public class OrderService {
        public void process() {
            Database.getInstance().save(...);  // Hidden dependency!
        }
    }
    
  2. Testability
    // Impossible to mock
    @Test
    void testOrder() {
        // Singleton preserves state between tests!
        // Impossible to substitute Database.getInstance()
    }
    
  3. SRP Violation
    // The class is responsible for:
    // 1. Business logic
    // 2. Managing its own lifecycle
    // 3. Concurrent access
    
  4. Memory Leak
    // Singleton lives forever (static field)
    // In Tomcat on redeploy → old ClassLoader not GC
    // → Metaspace Leak
    

Bill Pugh Singleton (Static Holder)

public class BillPughSingleton {
    private BillPughSingleton() {
        if (INSTANCE_HOLDER.INSTANCE != null) {
            throw new IllegalStateException("Already initialized");
        }
    }

    private static class INSTANCE_HOLDER {
        static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance() {
        return INSTANCE_HOLDER.INSTANCE;
    }
}

// JVM guarantees:
// 1. Laziness (class loads only on first call)
// 2. Thread-safety (class initialization is thread-safe)
// 3. No synchronized overhead

GC and Singleton

// Singleton will NOT be GC'd while:
// 1. ClassLoader is alive
// 2. Reference to static field exists

// In standard Java: ClassLoader lives until process ends
// → Singleton lives "forever"

// In Tomcat/OSGi: ClassLoader can be unloaded
// → But Singleton prevents GC → Memory Leak

Production Experience

Real scenario #1: Singleton killed testability

  • 500 tests, all use Config.getInstance()
  • Problem: tests depend on each other (shared state)
  • Solution: migrated to Spring DI
  • Result: isolated tests, parallel execution

Real scenario #2: Singleton in a cluster

  • ID generator as Singleton
  • Problem: 5 nodes in cluster → 5 generators → duplicate IDs!
  • Solution: Redis INCR for ID generation
  • Lesson: Singleton != distributed uniqueness

Best Practices

  1. DON’T write Singleton manually in Spring applications
  2. Use DI (@Component = Singleton scope)
  3. Enum — safest for libraries/SDKs
  4. Bill Pugh — for lazy initialization
  5. Avoid mutable state in Singleton
  6. readResolve() for serialization protection
  7. Constructor check for reflection protection
  8. In clusters → Redis/Zookeeper for coordination

Senior Summary

  • Singleton != distributed uniqueness — only within a JVM
  • Enum — the only protection from reflection/serialization attacks
  • DI container replaces manual Singleton in 99% of cases
  • Memory Model: volatile is critical for DCL
  • Testability: Singleton = enemy #1 of unit tests
  • GC: Singleton lives forever — be careful with resources
  • Bill Pugh = laziness + thread-safety without synchronized
  • Hidden dependencies = tight coupling = technical debt

Interview Cheat Sheet

Must know:

  • Singleton guarantees a single class instance through private constructor + static access method
  • Enum Singleton — safest: protection from reflection and serialization out of the box
  • Bill Pugh (Static Holder) — lazy initialization without synchronized, via static inner class
  • In Spring @Component = Singleton scope by default, DON’T write Singleton manually
  • Singleton doesn’t work in a cluster — each node creates its own instance
  • Without volatile in DCL, reordering is possible: thread gets uninitialized object
  • Singleton is considered an anti-pattern: violates SRP, DIP, testability

Common follow-up questions:

  • How to “break” Singleton? — Reflection (create new instance), serialization (deserialization creates new object), ClassLoader (multiple loaders)
  • Why is Enum better than ordinary Singleton? — JVM forbids reflectively creating enums, serialization works correctly
  • When is Singleton acceptable? — Loggers, configuration, caches — stateless or immutable objects
  • What is Bill Pugh Singleton? — Lazy initialization via static inner class, JVM guarantees thread-safety

Red flags (DO NOT say):

  • “I use Singleton for storing state in Spring” — Spring manages this through DI
  • “Singleton works in a cluster” — each node creates its own instance
  • “My Singleton is thread-safe without volatile” — DCL without volatile = undefined behavior
  • “Singleton is the best pattern” — it’s one of the most criticized patterns

Related topics:

  • [[04. How to implement thread-safe Singleton]] — implementation approaches
  • [[05. What is double-checked locking]] — Singleton optimization
  • [[06. What are problems with Singleton]] — anti-patterns and issues
  • [[02. What pattern categories exist]] — Singleton as a Creational pattern
  • [[16. What anti-patterns do you know]] — Singleton as a source of problems