Comprehensive Guide to the Singleton Pattern in Java: Implementations, Pitfalls, and the Secure Enum Solution
This article explains various Java singleton implementations—including eager, lazy, double‑checked locking, static inner class, and enum—demonstrates their thread‑safety issues, shows how reflection and serialization can break them, and presents the enum approach as a truly safe solution.
Singleton is one of the simplest and most fundamental design patterns, yet its correct implementation in Java involves many subtle details that can affect thread safety and security.
Eager (Hungry) Singleton
The eager singleton creates the instance when the class is loaded, guaranteeing thread safety without synchronization.
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry() {}
private static SingletonHungry getInstance() {
return instance;
}
}Test code runs multiple threads that call SingletonHungry.getInstance() and prints the same instance each time. The advantage is simplicity and thread safety; the drawback is that the instance is created even if never used, wasting memory.
Lazy Singleton
The lazy version delays instance creation until it is first requested, but the naïve implementation is not thread‑safe.
public class SingletonLazy {
private static SingletonLazy instance = null;
private SingletonLazy() {}
public static SingletonLazy getInstance() {
if (instance == null) {
return new SingletonLazy();
}
return instance;
}
}Running the same multithreaded test shows different objects being created, confirming the lack of thread safety.
Double‑Checked Locking (DCL)
DCL attempts to combine lazy initialization with thread safety by checking the instance twice, once before acquiring a lock and once inside the synchronized block.
public class SingleTonDcl {
private static SingleTonDcl instance = null;
private SingleTonDcl() {}
public static SingleTonDcl getInstance() {
if (instance == null) {
synchronized (SingleTonDcl.class) {
if (instance == null) {
instance = new SingleTonDcl();
}
}
}
return instance;
}
}Although it appears correct, the JVM may reorder the three steps of object creation (memory allocation, instance initialization, reference assignment), leading to a partially constructed object being visible to other threads. Adding volatile to the instance field prevents this reordering.
Static Inner Class Singleton
This technique leverages the class‑loading mechanism: the inner static class holds the instance, which is created only when the outer class’s getInstance() method is invoked.
public class SingleTonStaticInnerClass {
private SingleTonStaticInnerClass() {}
private static class HandlerInstance {
private static SingleTonStaticInnerClass instance = new SingleTonStaticInnerClass();
}
public static SingleTonStaticInnerClass getInstance() {
return HandlerInstance.instance;
}
}It provides lazy initialization without synchronization overhead and works across JDK versions, but it is still vulnerable to reflection attacks.
Unsafe Singletons: Reflection & Serialization Attacks
Even well‑written singletons can be broken via reflection by accessing private constructors, or via serialization by deserializing a previously written object, which creates a new instance.
// Reflection attack on DCL
Class<SingleTonDcl> clazz = SingleTonDcl.class;
Constructor<SingleTonDcl> ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true);
SingleTonDcl reflected = ctor.newInstance();
SingleTonDcl normal = SingleTonDcl.getInstance();
System.out.println(reflected);
System.out.println(normal);Similarly, serializing SingletonHungry and then deserializing produces a distinct object unless readResolve() is overridden to return the original instance.
Enum‑Based Singleton (Truly Safe)
Using an enum guarantees a single instance, protects against reflection, serialization, and cloning.
public enum SingleTonEnum {
/** instance */
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}Attempting to create an enum instance via reflection throws Cannot reflectively create enum objects, confirming its robustness.
Conclusion
The article summarizes various singleton implementations, highlights common pitfalls such as thread‑safety issues, reflection, and serialization attacks, and recommends the enum approach as the only truly safe singleton pattern in Java.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
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.
