🌐 What is the Singleton Pattern?
Singleton is a creational design pattern that ensures:
- Only one instance of a class can exist.
- That instance is accessible globally.
💡 Why use Singleton?
✅ To control shared access to resources (like configuration, logging, database connection).
✅ To enforce a single point of control.
✅ Saves memory in cases where only one instance is required.
🔗 Key Properties of Singleton
Property | Meaning |
---|---|
Single Instance | Only one object of the class exists. |
Global Access | The instance is accessible via a global/static method. |
Lazy or Eager Initialization | Instance can be created on-demand (lazy) or at class loading time (eager). |
📜 Example — Basic Singleton (Eager Initialization)
public class Logger {
private static final Logger INSTANCE = new Logger();
private Logger() {} // private constructor prevents external instantiation
public static Logger getInstance() {
return INSTANCE;
}
public void log(String message) {
System.out.println(message);
}
}
Logger logger = Logger.getInstance();
logger.log("Application started");
📦 Lazy Initialization Singleton (On Demand)
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
⚠️ This is not thread-safe — two threads could both see null
and create two instances.
🔒 Thread-Safe Singleton (Double-Checked Locking)
public class ConfigManager {
private static volatile ConfigManager instance;
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) { // First check (no locking)
synchronized (ConfigManager.class) {
if (instance == null) { // Second check (inside lock)
instance = new ConfigManager();
}
}
}
return instance;
}
}
✅ volatile
ensures visibility across threads.
✅ Double-checked locking minimizes synchronization overhead.
🚀 Best Practice — Bill Pugh Singleton (Inner Static Helper Class)
public class AppConfig {
private AppConfig() {}
private static class Holder {
private static final AppConfig INSTANCE = new AppConfig();
}
public static AppConfig getInstance() {
return Holder.INSTANCE;
}
}
✅ This is the cleanest, most recommended approach.
✅ Thread-safe without synchronization cost.
✅ Relies on JVM’s classloader to guarantee safe lazy loading.
🔥 Summary Table
Approach | Pros | Cons |
---|---|---|
Eager Initialization | Simple, thread-safe | Instance always created (even if not used) |
Lazy Initialization | Only created when needed | Not thread-safe (unless synchronized) |
Double-Checked Locking | Thread-safe, efficient | Slightly complex |
Bill Pugh Singleton | Best of both worlds (lazy + efficient + safe) | None really (preferred method) |
⚠️ Singleton in a Multithreaded Environment
- Be careful when using Singleton in multi-threaded apps — race conditions during creation can lead to multiple instances.
- Modern
Bill Pugh
approach avoids these problems.
💡 Common Singleton Use Cases
✅ Logging (Logger
)
✅ Configuration (ConfigManager
)
✅ Caches
✅ Database Connection Pools
✅ Service Locators
🛑 Singleton Anti-pattern Warning
- Overusing Singleton can lead to global state, which makes testing harder.
- In some cases, Dependency Injection is preferred over Singleton.
📚 Final Tip for Interviews
✅ Explain all approaches — Eager, Lazy, Double-Checked, Bill Pugh.
✅ Mention thread-safety challenges in multi-threaded code.
✅ Mention why overusing Singleton can hurt testability (global state).
✅ If asked best way in modern Java? — Say Bill Pugh (Static Holder Idiom).