In this tutorial, you’ll learn how to implement the Singleton Design Pattern in Java — one of the most popular Creational Design Patterns used to ensure that only one instance of a class exists throughout the application.
It’s widely used for cases like logging, database connections, configuration managers, and thread pools — where you only need one shared instance.
1. What Is the Singleton Design Pattern?
The Singleton Pattern ensures that a class has only one instance and provides a global point of access to it.
In simple terms: It’s a single object shared across the application.
2. Real-World Analogy
Think of a Printer Spooler — no matter how many times you hit print, there’s only one central spooler that manages all print jobs. That’s how the Singleton pattern works — it ensures only one instance handles all operations.
3. Basic Implementation (Eager Initialization)
This is the simplest way to implement a Singleton — the instance is created at class loading time.
public class EagerSingleton {
// Instance created at class loading time
private static final EagerSingleton instance = new EagerSingleton();
// Private constructor prevents instantiation
private EagerSingleton() {
System.out.println("EagerSingleton instance created!");
}
// Global access point
public static EagerSingleton getInstance() {
return instance;
}
}
// Test it
public class Main {
public static void main(String[] args) {
EagerSingleton s1 = EagerSingleton.getInstance();
EagerSingleton s2 = EagerSingleton.getInstance();
System.out.println(s1 == s2); // true
}
}
Output:
EagerSingleton instance created!
true
4. Lazy Initialization (Creates Only When Needed)
In the lazy version, the instance is created only when it’s first requested — not at class load time.
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
System.out.println("LazySingleton instance created!");
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
Note: This version is not thread-safe — multiple threads could create multiple instances simultaneously.
5. Thread-Safe Singleton (Synchronized Method)
To make it thread-safe, you can synchronize the access method.
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
System.out.println("ThreadSafeSingleton instance created!");
}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
This ensures only one thread can access the method at a time. However, synchronization can make it slower under heavy load.
6. Double-Checked Locking (Best Practice)
This is the most efficient and widely used thread-safe Singleton implementation.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
System.out.println("Singleton instance created!");
}
public static Singleton getInstance() {
if (instance == null) { // first check
synchronized (Singleton.class) {
if (instance == null) { // second check
instance = new Singleton();
}
}
}
return instance;
}
}
Why use volatile?
It ensures that multiple threads handle the instance variable correctly during initialization.
7. Singleton Using Enum (Simplest & Most Secure Way)
Java provides a built-in way to create Singleton using enum — it’s thread-safe, serialization-safe, and protected from reflection attacks.
public enum EnumSingleton {
INSTANCE;
public void showMessage() {
System.out.println("Hello from Enum Singleton!");
}
}
// Test
public class Main {
public static void main(String[] args) {
EnumSingleton singleton = EnumSingleton.INSTANCE;
singleton.showMessage();
}
}
Output:
Hello from Enum Singleton!
8. Advantages of Singleton Pattern
- Ensures a single shared instance across the application.
- Controls concurrent access to shared resources.
- Saves memory — only one instance is created.
- Used heavily in logging, caching, and configuration management.
9. Common Use Cases
- Database connection management
- Logging frameworks
- Thread pools
- Application-wide configuration or cache
10. Pitfalls to Avoid
- Improper synchronization can lead to multiple instances in multithreaded apps.
- Overuse of Singleton can lead to tight coupling and testing difficulties.
11. Conclusion
The Singleton Design Pattern is one of the simplest and most useful patterns in software design. It ensures controlled access to shared resources and maintains consistency across your application.
Use it carefully — it’s powerful when used in the right scenarios!
Next Step: Explore the Factory Design Pattern and Builder Design Pattern to deepen your understanding of object creation techniques.
0 Comments