Double-Checked Locking Singleton Pattern in Java
Double-checked locking provides lazy initialization with lower synchronization overhead after initialization.
Real-World Use Cases
- expensive configuration object
- app-level registry / metadata cache
- shared parser/serializer setup
Java Implementation
public final class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Safer Alternative
public enum AppSingleton {
INSTANCE;
}
Use enum singleton when possible.
Production API Equivalent (Dependency Injection Singleton)
In most backend applications, prefer framework-managed singletons over hand-written singleton patterns.
Spring example:
import org.springframework.stereotype.Service;
@Service
public class CurrencyRateService {
public double convert(double amount, double rate) {
return amount * rate;
}
}
@Service beans are singleton-scoped by default, test-friendly, and easier to evolve than static/global singleton holders.
Java 8/11/17/21/25 Notes
- Java 8+: double-checked locking is safe with
volatile. - JDK 11 / Java 17: same guidance, widely used in enterprise LTS environments.
- Java 21+: same guidance, no behavioral change.
- Java 25: no expected API or memory-model change for this pattern.
Key Takeaways
volatileis mandatory in double-checked locking.- Prefer enum singleton for simplicity and serialization safety.
- Use lazy singleton only when initialization cost justifies it.