Understanding the Singleton Pattern: Lazy and Eager Implementations in Java
This article explains the Singleton design pattern, compares lazy and eager implementations in Java, discusses thread‑safety issues with double‑checked locking, presents complete code examples, and outlines the advantages, disadvantages, and appropriate use‑cases for singletons.
The Singleton pattern ensures that a class has only one globally accessible instance, which is useful for coordinating shared resources such as a project manager in a software system.
Lazy (lazy‑initialization) implementation : The instance is created only when getInstance() is first called. The basic version uses a private constructor and a static field, but it is not thread‑safe because multiple threads can create separate instances simultaneously.
Code example (non‑thread‑safe):
public class ProjectManager {
private static ProjectManager instance;
private ProjectManager() {}
public static ProjectManager getInstance() {
if (instance == null) {
instance = new ProjectManager();
}
return instance;
}
}To make it thread‑safe, double‑checked locking with synchronized and a volatile instance field is used. The first if (instance == null) avoids unnecessary synchronization after the instance is created, while the second check inside the synchronized block guarantees that only one thread creates the object.
Thread‑safe lazy implementation:
public class ProjectManager {
private static volatile ProjectManager instance;
private ProjectManager() {}
public static ProjectManager getInstance() {
if (instance == null) {
synchronized (ProjectManager.class) {
if (instance == null) {
instance = new ProjectManager();
}
}
}
return instance;
}
}Client code demonstrating that two references point to the same instance:
ProjectManager zhangsan = ProjectManager.getInstance();
ProjectManager lisi = ProjectManager.getInstance();
if (zhangsan == lisi) {
System.out.println("Both managers refer to the same instance");
}Eager (hungry) implementation : The instance is created when the class is loaded, guaranteeing thread safety without synchronization.
public class ProjectManager {
private static final ProjectManager instance = new ProjectManager();
private ProjectManager() {}
public static ProjectManager getInstance() {
return instance;
}
}Advantages of the Singleton pattern include reduced memory usage, controlled access through a single entry point, and easy sharing of a central object across threads. Disadvantages involve potential violation of the Single Responsibility Principle, hidden dependencies, and the risk of thread‑safety problems when mutable state is shared.
Typical use‑cases are centralized coordination components (e.g., resource managers) and utility classes that have no instance state. Overusing singletons can lead to excessive coupling and difficult testing, so they should be applied only when a globally unique object is truly required.
In summary, the lazy double‑checked locking version is recommended for most scenarios because it creates the instance on demand while remaining thread‑safe; the eager version is simpler but may waste memory if the singleton is never used.
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.