Understanding Double-Checked Locking Singleton Pattern in Java
This article explains the double‑checked locking design pattern for thread‑safe singletons in Java, demonstrates its performance drawbacks, and presents alternative implementations such as early initialization, init‑on‑demand holder, and enum‑based singletons, highlighting their advantages and limitations.
In a previous article we discussed converting asynchronous queries to synchronous calls for server‑side performance; this piece focuses on the double‑checked locking (DCL) design pattern, which reduces lock acquisition by first checking a condition before entering a synchronized block, thereby improving performance.
Singleton Lock Demo
First, a straightforward singleton with a synchronized getInstance method is shown, which is thread‑safe but incurs unnecessary lock overhead on every call.
public class DraconianSingleton {
private static DraconianSingleton instance;
public static synchronized DraconianSingleton getInstance() {
if (instance == null) {
instance = new DraconianSingleton();
}
return instance;
}
// other methods
}To address the performance issue, the double‑checked locking pattern adds a preliminary null check and a second check inside a synchronized block, requiring the instance field to be declared volatile to avoid cache‑inconsistency problems.
public class DclSingleton {
private static volatile DclSingleton instance;
public static DclSingleton getInstance() {
if (instance == null) {
synchronized (DclSingleton.class) {
if (instance == null) {
instance = new DclSingleton();
}
}
}
return instance;
}
// other methods
}While DCL can speed up access, it has two major drawbacks: it requires the volatile keyword (incompatible with Java 1.4 and earlier) and its verbosity makes the code harder to read.
Alternative Approaches
Because of these issues, other strategies that delegate synchronization to the JVM are explored.
Early Initialization
The simplest thread‑safe method is to create the instance eagerly as a static final field, leveraging the Java Language Specification (JLS 12.4.2) which guarantees class initialization order.
public class EarlyInitSingleton {
private static final EarlyInitSingleton INSTANCE = new EarlyInitSingleton();
public static EarlyInitSingleton getInstance() {
return INSTANCE;
}
// other methods
}Init‑On‑Demand Holder
Using a nested static class defers instance creation until getInstance is first called, because the inner class is only loaded on demand.
public class InitOnDemandSingleton {
private static class InstanceHolder {
private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton();
}
public static InitOnDemandSingleton getInstance() {
return InstanceHolder.INSTANCE;
}
// other methods
}Enum Singleton
The most concise and safest way to implement a singleton in Java is to use an enum, which inherently provides serialization safety and guarantees a single instance.
public enum EnumSingleton {
INSTANCE;
// other methods
}Conclusion
Overall, the double‑checked locking pattern is limited by verbosity and lack of backward compatibility, making it error‑prone; developers are encouraged to prefer JVM‑managed synchronization alternatives such as early initialization, the holder idiom, or enum singletons.
FunTester
10k followers, 1k articles | completely useless
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.