In software engineering, the Singleton pattern is a creational design pattern that ensures the presence of only one instance of a class in a system. This pattern restricts the instantiation of a class to a single object and provides global access to that object. It is widely used when a single instance of a class is required to coordinate actions across a system.
The basic Singleton pattern involves creating a class with a private constructor to prevent direct instantiation of objects and providing a static method that returns the unique instance of the class. The class itself holds a private static reference to the single instance, which is lazily initialized upon the first request.
public class Singleton {
private static Singleton instance;
private Singleton() {
// Private constructor to prevent instantiation
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
The basic Singleton pattern ensures that only one instance of the Singleton class can exist in the system. However, it is not thread-safe, and multiple threads accessing the getInstance()
method simultaneously might create multiple instances.
To overcome the lack of thread-safety in the basic Singleton pattern, several variations can be implemented. One approach is to use synchronized methods or blocks to lock access to the Singleton instance during the creation process.
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
// Private constructor to prevent instantiation
}
public synchronized static ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
While the synchronized approach ensures thread-safety, it can introduce a performance bottleneck. To avoid the overhead of synchronization every time getInstance()
is called, we can use the concept of double-checked locking.
The double-checked locking Singleton pattern improves performance by checking the instance existence without acquiring a lock first. It minimizes the synchronized block's duration by checking the instance inside and outside the synchronized block.
public class DoubleCheckedSingleton {
private volatile static DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {
// Private constructor to prevent instantiation
}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
The addition of the volatile
keyword ensures that changes made to the instance
variable are visible across all threads.
Introduced in Java 5, the Enum Singleton pattern provides a concise and thread-safe implementation. Enums in Java are singletons by default, making the pattern simple to implement.
public enum EnumSingleton {
INSTANCE;
// methods and fields
}
By using the Enum Singleton pattern, the JVM guarantees the creation of a single instance for each Enum value, even in the presence of multiple class loaders or simultaneous access from different threads.
The Singleton pattern provides a flexible and efficient way to ensure a class has only one instance throughout the application. Although the basic Singleton pattern lacks thread-safety, variations like the Thread-Safe Singleton, Double-Checked Locking Singleton, and Enum Singleton address this concern by accounting for concurrency issues. Understanding and utilizing these variations appropriately enables effective utilization of the Singleton pattern in Java applications.
noob to master © copyleft