Singleton Pattern
BeginnerEnsures a class has only one instance and provides a global access point to it.
Overview
The Singleton pattern restricts instantiation of a class to a single object. It is one of the most commonly used — and misused — patterns in Java. The naive approach (lazy initialization without synchronization) breaks under multithreading. Production implementations use Double-Checked Locking with a volatile field, or the Initialization-on-Demand Holder idiom, or an enum. Singletons are used for configuration managers, thread pools, caches, and logging. Overuse leads to hidden coupling and testability issues, so prefer dependency injection when possible.
Naive vs Thread-Safe Implementations
Eager initialization is the simplest thread-safe form but creates the instance even if never used. Lazy initialization without synchronization breaks under concurrent access — two threads can simultaneously pass the null check and create two instances. Double-Checked Locking with volatile fixes this efficiently.
// 1. Eager initialization — thread-safe, but instance always created
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {} // private constructor
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
// 2. Lazy + Double-Checked Locking — thread-safe and lazy
public class LazySingleton {
// volatile prevents instruction reordering (partial construction visibility)
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // first check (no lock)
synchronized (LazySingleton.class) {
if (instance == null) { // second check (with lock)
instance = new LazySingleton();
}
}
}
return instance;
}
}
// 3. Initialization-on-Demand Holder — elegant, lazy, thread-safe
public class HolderSingleton {
private HolderSingleton() {}
private static class Holder {
// Loaded only when Holder is accessed; class loading is thread-safe
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE;
}
}Enum Singleton (Best Practice)
Joshua Bloch (Effective Java) recommends enum singletons. The JVM guarantees that enum values are instantiated exactly once per JVM and are inherently serialization-safe. Reflection attacks cannot break an enum singleton because the JVM prohibits instantiating enum types reflectively.
// 4. Enum Singleton — serialization-safe, reflection-safe
public enum EnumSingleton {
INSTANCE;
private int connectionCount = 0;
public void connect() {
connectionCount++;
System.out.println("Connected. Total: " + connectionCount);
}
public int getConnectionCount() {
return connectionCount;
}
}
// Usage
EnumSingleton.INSTANCE.connect();
EnumSingleton.INSTANCE.connect();
System.out.println(EnumSingleton.INSTANCE.getConnectionCount()); // 2
// Breaking naive singletons with reflection (does NOT work on enum)
// Constructor con = LazySingleton.class.getDeclaredConstructor();
// con.setAccessible(true);
// LazySingleton second = (LazySingleton) con.newInstance(); // breaks DCL singleton
// EnumSingleton second = EnumSingleton.class.getDeclaredConstructor().newInstance();
// ↑ throws IllegalArgumentException — enum is safeSingleton in Spring & Pitfalls
Spring beans are singleton-scoped by default within the application context. This is NOT the GoF Singleton — it is one instance per container, not per JVM. Common pitfalls: mutable shared state causes race conditions; Singleton holding a reference to a prototype bean locks the prototype to one instance unless using ApplicationContext.getBean() or @Lookup.
@Service // Spring singleton bean — one instance per ApplicationContext
public class ConfigService {
private final Map<String, String> cache = new ConcurrentHashMap<>(); // thread-safe!
public String getConfig(String key) {
return cache.computeIfAbsent(key, this::loadFromDb);
}
private String loadFromDb(String key) {
// simulate DB lookup
return "value-for-" + key;
}
}
// Anti-pattern: Singleton with mutable non-thread-safe state
@Service
public class BadSingleton {
private List<String> requestLog = new ArrayList<>(); // NOT thread-safe!
public void log(String entry) {
requestLog.add(entry); // race condition under concurrent requests
}
}Key Points to Remember
- 1volatile is mandatory in Double-Checked Locking; without it the JVM may publish a partially constructed object.
- 2Enum singleton is the safest approach — immune to reflection and serialization attacks.
- 3The Initialization-on-Demand Holder idiom is lazy, thread-safe, and has no synchronization overhead.
- 4Spring @Service/@Component beans are singletons per ApplicationContext, not per JVM.
- 5Singletons with mutable state require thread-safe data structures (ConcurrentHashMap, AtomicInteger).
- 6Prefer dependency injection over global Singleton access to improve testability.
Interview Questions
Sign in to ask AriaWhy must the instance field be volatile in Double-Checked Locking?
How does an enum singleton prevent reflection attacks?
What is the difference between a Spring singleton bean and the GoF Singleton pattern?
How would you break a naive singleton implementation and how do you prevent it?
When would you NOT use the Singleton pattern?
Ask Aria about Singleton Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.