Singleton Design Pattern | Java
The Singleton Design Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. This pattern is useful when you need exactly one instance of a class to control access to a shared resource.
Key Characteristics of Singleton Pattern
- Single Instance: Guarantees that only one instance of the class is created.
- Global Access Point: Provides a global access point to the instance.
- Controlled Access: Controls the access to the instance, often providing methods to manage its creation and lifecycle.
Common Uses of Singleton Pattern
- Database Connection: Managing a single database connection instance to avoid multiple connections and ensure efficient resource use.
- Configuration Management: Managing configuration settings for an application from a single source.
- Logging: Centralizing logging to ensure all log entries are managed by a single instance.
Here are explanations and Java implementations for a simple singleton, a thread-safe singleton, and a singleton that prevents cloning.
- Simple Singleton: Ensures a single instance with a private constructor and a public static method.
- Thread-Safe Singleton: Uses synchronized methods or double-checked locking to handle concurrent access.
- Non-Cloneable Singleton: Prevents cloning by overriding the
clone
method to throw an exception.
1. Simple Singleton Implementation
A simple implementation of the Singleton pattern involves a private static instance of the class and a public static method to provide access to this instance.
public class Singleton {
// Private static variable of the single instance
private static Singleton instance;
// Private constructor to prevent instantiation
private Singleton() {}
// Public static method to provide access to the instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// Method for demonstration
public void showMessage() {
System.out.println("Hello from Singleton!");
}
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
Explanation:
- Private Constructor: Ensures no other class can instantiate the Singleton class.
- Static Instance: Holds the single instance of the class.
- Public Static Method: Provides a global point of access to the instance.
2. Thread-Safe Singleton Implementation
To make the singleton thread-safe, you can use synchronized methods or blocks.
Synchronized Method:
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
// Synchronized method to control simultaneous access
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from ThreadSafeSingleton!");
}
public static void main(String[] args) {
ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
singleton.showMessage();
}
}
Double-Checked Locking:
public class ThreadSafeSingleton {
private static volatile ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static ThreadSafeSingleton getInstance() {
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
public void showMessage() {
System.out.println("Hello from ThreadSafeSingleton!");
}
public static void main(String[] args) {
ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
singleton.showMessage();
}
}
Explanation:
- Synchronized Method: Ensures that only one thread can execute the
getInstance
method at a time. - Double-Checked Locking: Minimizes the performance cost of synchronization by first checking if the instance is
null
without locking.
3. Singleton that Prevents Cloning
To prevent a singleton instance from being cloned, implement the Cloneable
interface and override the clone
method to throw a CloneNotSupportedException
.
public class NonCloneableSingleton implements Cloneable {
private static NonCloneableSingleton instance;
private NonCloneableSingleton() {}
public static NonCloneableSingleton getInstance() {
if (instance == null) {
instance = new NonCloneableSingleton();
}
return instance;
}
// Override clone method to prevent cloning
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton instance cannot be cloned");
}
public void showMessage() {
System.out.println("Hello from NonCloneableSingleton!");
}
public static void main(String[] args) {
NonCloneableSingleton singleton = NonCloneableSingleton.getInstance();
singleton.showMessage();
try {
NonCloneableSingleton clonedSingleton = (NonCloneableSingleton) singleton.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
}
}
Explanation:
- Override
clone
Method: Throws aCloneNotSupportedException
to ensure the singleton instance cannot be cloned.
Disadvantages of Singleton Pattern
- Global State: Introduces a global state into the application, which can lead to issues with testing and maintainability.
- Concurrency Issues: May cause concurrency issues if not properly synchronized.
- Hidden Dependencies: Can hide dependencies, making the code less transparent and harder to understand.
Summary
The Singleton Design Pattern is useful when you need a single, globally accessible instance of a class. It is commonly used for managing configuration settings, logging, and database connections. However, it should be used with care due to potential drawbacks like global state and concurrency issues.