Singleton Design Pattern
Table of Content
- Features of Singleton Pattern
- Usages of Singleton Design Pattern
- Ways to Implement Singleton Design Pattern
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one. This is useful when exactly one object is needed to coordinate actions across the system.
Features of Singleton Pattern
- Private constructor – to restrict initialization of the class from other classes
- Single instance – should only have one instance of the class available to outside world
- Global access point that returns the only instance
Usages of Singleton Design Pattern
- Hardware interface access: The use of singleton depends on the requirements. However practically singleton can be used in case external hardware resource usage limitation required e.g. Hardware printers where the print spooler can be made a singleton to avoid multiple concurrent accesses and creating deadlock.
- Logger : Similarly singleton is a good potential candidate for using in the log files generation. Imagine an application where the logging utility has to produce one log file based on the messages received from the users. If there is multiple client application using this logging utility class they might create multiple instances of this class and it can potentially cause issues during concurrent access to the same logger file. We can use the logger utility class as a singleton and provide a global point of reference.
- Configuration File: This is another potential candidate for Singleton pattern because this has a performance benefit as it prevents multiple users to repeatedly access and read the configuration file or properties file. It creates a single instance of the configuration file which can be accessed by multiple calls concurrently as it will provide static config data loaded into in-memory objects. The application only reads from the configuration file at the first time and there after from second call onwards the client applications read the data from in-memory objects.
- Cache: We can use the cache as a singleton object as it can have a global point of reference and for all future calls to the cache object the client application will use the in-memory object.
Ways to Implement Singleton Design Pattern
Eager Initialization
In eager initialization, the instance of singleton class is created at the time of class loading or system startup. This is the simplest method to create singleton classes.
/**
* This is an example of singleton class using eager initialization.
*/
public class EagerInitializationExample {
private static volatile EagerInitializationExample instance = new EagerInitializationExample();
private EagerInitializationExample() {
}
/**
* Returns the only instance of the class created during initialization.
*/
public static EagerInitializationExample getInstance() {
return instance;
}
}
However, it has few drawbacks.
- Instance is created even though the application might not be using it. If this instance is not a big object and we can live with it being unused, then this is the best approach.
- This method doesn’t provide a framework for exception handling during initialization. It may have failed to initialize the instance during start up, but during runtime it will give a null response and we won’t be able to debug until we look at the start up logs.
Static Block Initialization
Static block initialization implementation is similar to eager initialization, except for the fact that instance of class is created in the static block that provides option for exception handling.
/**
* This is an example of singleton class using static block initialization.
*/
public class StaticBlockExample {
private static final StaticBlockExample INSTANCE;
private StaticBlockExample() {
}
/* This block of code is called even before the constructor is called */
static {
try {
INSTANCE = new StaticBlockExample();
} catch (Exception e) {
// do something
throw new RuntimeException();
}
}
/**
* Returns the only instance of the class created during initialization.
*/
public static StaticBlockExample getInstance() {
return INSTANCE;
}
}
Both eager initialization and static block initialization creates the instance even before it’s being used and that is not considered as a best practice.
Lazy Initialization
Lazy initialization method to implement singleton pattern creates the instance during the first global access and then shares the same for all upcoming requests.
/**
* This is an example of singleton class using lazy initialization.
*/
public class LazyInitializationExample {
private static volatile LazyInitializationExample instance;
private LazyInitializationExample() {
}
/**
* Returns the only instance of the class created if null.
*/
public static LazyInitializationExample getInstance() {
if (instance == null) {
instance = new LazyInitializationExample();
}
return instance;
}
}
Even though lazy initialization method is efficient in term of when to initialize an instance (upon first request), this implementation only works fine for a single threaded environment. However, when it comes to multi-threaded systems, it could cause issues if multiple threads are inside the loop at the same time. It will destroy the singleton pattern and both threads will get different instances of the singleton class.
Suppose there are two threads T1 and T2. Both came to create the instance and checks if “instance==null”. Now both threads have identified instance variable as null thus they both assume they must create an instance. In the end, we have two instances in our application.
Thread Safe Singleton
The simplest way to create a thread safe singleton class is to make the global access method synchronized. So that only one thread can execute this method at a time.
/**
* This is an example of singleton class using thread safe initialization.
*/
public class ThreadSafeExample {
private static volatile ThreadSafeExample instance;
private ThreadSafeExample() {
}
/**
* Returns the only instance of the class created during initialization.
*/
public static synchronized ThreadSafeExample getInstance() {
if (instance == null) {
instance = new ThreadSafeExample();
}
return instance;
}
}
Above implementation works fine and provides thread safety but it impacts the performance because of the cost associated with synchronized method. Although we need it only for the first few threads which might create the separate instance, it impacts all the request made to getInstance() method. We can avoid this by using a double checked locking principle
as shown below.
/**
* This is an example of singleton class using double checked locking initialization.
*/
public class DoubleCheckedLockingExample {
private static volatile DoubleCheckedLockingExample instance;
private DoubleCheckedLockingExample() {
}
/**
* Returns the only instance of the class created during initialization.
*/
public static DoubleCheckedLockingExample getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingExample.class) {
if (instance == null) {
instance = new DoubleCheckedLockingExample();
}
}
}
return instance;
}
}
Bill Pugh Singleton
Prior to Java 5, java memory model had a lot of issues and above approaches used to fail in certain scenario where too many threads try to get the instance of singleton class simultaneously. The Bill Pugh Singleton implementation solves the problem. His principle “Initialization-on-demand holder idiom” also uses the static block idea, but in a different way. It suggests to use static inner class.
/**
* This is an example of singleton class using Bill Pugh initialization.
*/
public class BillPughSingletonExample {
private BillPughSingletonExample() {
}
private static class BillPughSingletonHolder {
private static final BillPughSingletonExample INSTANCE = new BillPughSingletonExample();
}
/**
* Returns the singleton instance of the class.
*/
public static BillPughSingletonExample getInstance() {
return BillPughSingletonHolder.INSTANCE;
}
}
When the singleton class is loaded, the static holder class is not loaded into the memory. Only when a request is made to getInstance() method, this class gets loaded and creates a singleton instance. This is most widely used approach for singleton class as it doesn’t require synchronization.
Source code on Github