Double‑Checked Locking, Static Inner Class, and Enum Singleton Implementations in Java
This article explains three Java singleton implementations—double‑checked locking with volatile, static inner‑class holder, and enum singleton—detailing their thread‑safety, lazy initialization, and how they prevent issues such as instruction reordering, serialization, and reflection attacks.
1. Double‑Checked Locking (DCL) Singleton
The DCL pattern provides thread‑safe lazy initialization by checking the instance twice, once without synchronization and once inside a synchronized block, and requires the instance variable to be declared volatile to prevent instruction reordering.
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}Without the second check, two threads could create separate instances. The volatile keyword ensures that the write to singleton is visible to other threads and prevents the JVM from reordering the object construction steps.
Why double check?
If two threads enter getInstance() simultaneously, the first thread creates the instance inside the synchronized block; without the second if , the second thread would also create a new instance after the lock is released, breaking the singleton guarantee.
Why volatile ?
Object creation involves three steps: memory allocation, constructor execution, and assigning the reference. Without volatile , the last two steps can be reordered, leading to a partially constructed object being observed by other threads. Since JDK 1.5, volatile prevents this reordering.
2. Static Inner‑Class Singleton
This approach leverages the class‑loading mechanism: the inner static class holds the singleton instance, which is created only when the outer class's getInstance() method is called.
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.singleton;
}
}It offers lazy loading and high efficiency similar to DCL, but like DCL it cannot prevent multiple instances caused by serialization or reflection.
3. Enum Singleton (Recommended)
Enum singletons are inherently thread‑safe, handle lazy initialization automatically, and protect against serialization and reflection attacks because the JVM ensures a single enum instance.
public enum Singleton {
INSTANCE;
public void testMethod() {
// method body
}
}During serialization, only the enum name is written, and deserialization uses java.lang.Enum.valueOf to retrieve the existing instance, avoiding a new object. Reflection cannot instantiate an enum and throws java.lang.IllegalArgumentException: Cannot reflectively create enum objects .
Usage Example
// Singleton.java
public enum Singleton {
INSTANCE;
public void testMethod() {
System.out.println("Executed singleton method");
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Singleton.INSTANCE.testMethod();
System.out.println(Singleton.INSTANCE);
}
}Running the program prints the method message and the enum instance.
Decompiling the Enum Singleton
Decompiling with javap -p Singleton.class reveals that the enum defines a static field INSTANCE , a private static array $VALUES , and a static initializer that creates the instance. The static block contains bytecode instructions such as new , dup , invokespecial , and putstatic to instantiate and assign the enum constant, ensuring thread‑safety at class‑loading time.
public final class Singleton extends java.lang.Enum
{
public static final Singleton INSTANCE;
private static final Singleton[] $VALUES;
static {}
// ... other generated methods (values, valueOf, etc.)
}The static initializer creates the enum instance and populates the $VALUES array, guaranteeing that INSTANCE is constructed exactly once when the class is loaded.
In summary, while DCL and static inner‑class singletons provide lazy loading, the enum singleton is the most robust solution in Java because it automatically handles serialization, reflection, and thread‑safety without additional code.
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.