Java Singleton Pattern: Lazy, Eager, Static Inner Class, Enum, and Double-Checked Locking
This article explains the Java Singleton pattern, covering lazy (thread‑unsafe), eager, static inner‑class, enum, and double‑checked locking implementations, discusses their thread‑safety, performance, and serialization issues, and provides complete code examples for interview preparation.
The Singleton pattern ensures a class has only one instance and provides a global access point. Below are several common Java implementations and their characteristics.
Lazy (thread‑unsafe)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}This version works in multithreaded environments but incurs synchronization overhead on every call.
Eager
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}Instantiation occurs when the class is loaded, avoiding synchronization but creating the instance even if it is never used.
Static Inner Class
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}This leverages class‑loader mechanics to achieve lazy initialization without synchronization; the inner class is loaded only when getInstance is called.
Enum
public enum Singleton {
INSTANCE;
public void whateverMethod() {}
}Recommended by Joshua Bloch, this approach provides implicit thread safety, protects against serialization attacks, and is concise, though it requires Java 1.5 or later.
Double‑Checked Locking (JDK 1.5+)
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}This version achieves thread‑safe lazy initialization with minimal synchronization overhead, but the volatile keyword may affect performance and should be used only when necessary.
Summary
Two important pitfalls to watch for:
If different class loaders load the Singleton class, multiple instances can exist (e.g., separate servlet containers).
If the Singleton implements java.io.Serializable , deserialization can create additional instances unless readResolve is implemented.
Fixes:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = Singleton.class.getClassLoader();
}
return classLoader.loadClass(classname);
}And for serialization:
public class Singleton implements java.io.Serializable {
public static final Singleton INSTANCE = new Singleton();
protected Singleton() {}
private Object readResolve() { return INSTANCE; }
}These techniques ensure a true singleton across class loaders and serialization boundaries.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.