Implementing the Singleton Design Pattern
How will you ensure that a class has only one instance? There are several ways of doing it in Java.ย Following are the ways:
- Declare a private constructor to prevent others from making object of class.
- Create the object of the class either during class loading in a staticย block, or in a static method that first checks whether the object exists or not and creates a new object only if it doesnโt exist.
Letโs see all the possible solutions with code samples one by one:
1. Eagerly Initialized Singleton
This is the simplest approach in which object is created at the time of class loading –
public class EagerSingleton {
/** private constructor to prevent others from instantiating this class */
private EagerSingleton() {}
/** Create an instance of the class at the time of class loading */
private static final EagerSingleton instance = new EagerSingleton();
/** Provide a global point of access to the instance */
public static EagerSingleton getInstance() {
return instance;
}
}
The disadvantage of this approach is that the instance is created irrespective of whether it is used or not. It can have performance implication as object will be created without its use.
2. Eagerly Initialized Static Block Singleton
We can also create the one instance of the class in a static block. It works because the static block is executed only once at the time of class loading.
The benefit with static block initialization is that you can write your initialization logic or handle exceptions in the static block.
public class EagerStaticBlockSingleton {
private static final EagerStaticBlockSingleton instance;
/** Don't let anyone else instantiate this class */
private EagerStaticBlockSingleton() {}
/** Create the one-and-only instance in a static block */
static {
try {
instance = new EagerStaticBlockSingleton();
} catch (Exception ex) {
throw ex;
}
}
/** Provide a public method to get the instance that we created */
public static EagerStaticBlockSingleton getInstance() {
return instance;
}
}
Like the previous solution, the instance is created whether or not it is needed by the application.
3. Lazily Initialized Singleton
Lazy initialization means making object only when it is needed.
In the following example, we first check whether the object is already created or not in theย getInstance()
ย method. If the object is already created, we simply return it, otherwise, we first create the object and then return it:
public class LazySingleton {
private static LazySingleton instance;
/** Don't let anyone else instantiate this class */
private LazySingleton() {}
/** Lazily create the instance when it is accessed for the first time */
public static synchronized LazySingleton getInstance() {
if(instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
Focus on the use ofย synchronized
ย keyword in theย getInstance()
ย method. This is required to prevent race conditions in multi-threaded environments.
Letโs say that theย object
ย is not created yet, and two threads enter theย getInstance()
ย method simultaneously. In that case, theย instance==null
ย check will be equal to true and both the threads will create a new object of the class.
Theย synchronized
ย keyword ensures that only one thread can execute theย getInstance()
ย method at one time.
4. Lazily Initialized Double-Checked Locking Singleton
Theย synchronized
ย keyword in theย getInstance()
ย method prevents race conditions, but it also has some performance issue
Below is an optimized way of the lazily initialized singleton whereย instead of making the entire methodย synchronized
, we create aย synchronized
ย block and write only the instantiation part in theย synchronized
ย block –
public class LazyDoubleCheckedLockingSingleton {
private static volatile LazyDoubleCheckedLockingSingleton instance;
/** private constructor to prevent others from instantiating this class */
private LazyDoubleCheckedLockingSingleton() {}
/** Lazily initialize the singleton in a synchronized block */
public static LazyDoubleCheckedLockingSingleton getInstance() {
if(instance == null) {
synchronized (LazyDoubleCheckedLockingSingleton.class) {
// double-check
if(instance == null) {
instance = new LazyDoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
The above approach is calledย Double-Checked Lockingย because we double-check whether the variable is initialized or not inside theย synchronized
ย block.
The double-checking is very important here. Letโs take that two threadsย T1
ย andย T2
ย enter theย getInstance()
ย method at the same time. Theย instance==null
ย check will be equal to true, so both of them will enter into theย synchronized
ย block one-by-one. If the double check was not there, both threads would have created a new instance.
Also, focus on the use ofย volatile
ย keyword with the instance variable. This is important to prevent compilers from doing their own optimizations and handle the singleton correctly.
5. Lazily Initialized Inner Class Singleton (Bill Pugh singleton)
Bill Pugh came up with a highly efficient solution to create singleton. It is calledย Initialization-on-demand holder idiom. In this , a static inner class is used to lazily create a singleton object.
public class LazyInnerClassSingleton {
/** private constructor to prevent others from instantiating this class */
private LazyInnerClassSingleton() {}
/** This inner class is loaded only after getInstance() is called for the first time. */
private static class SingletonHelper {
private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
}
public static LazyInnerClassSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
Note that, the inner class is not loaded unless theย getInstance()
ย method is invoked for the first time. This way of solution is thread-safe and doesnโt require any synchronization. It isย most efficient solution among all the singleton design pattern implementations.
6. Enum Singleton
An Enum is singleton by design. All the enum values are initialized only once during class loading.
import java.util.Arrays;
/** An Enum value is initialized only once at the time of class loading.
It is singleton by design and is also thread-safe.
*/
enum EnumSingleton {
WEEKDAY("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"),
WEEKEND("Saturday", "Sunday");
private String[] days;
EnumSingleton(String ...days) {
System.out.println("Initializing enum with " + Arrays.toString(days));
this.days = days;
}
public String[] getDays() {
return this.days;
}
@Override
public String toString() {
return "EnumSingleton{" +
"days=" + Arrays.toString(days) +
'}';
}
}
public class EnumSingletonExample {
public static void main(String[] args) {
System.out.println(EnumSingleton.WEEKDAY);
System.out.println(EnumSingleton.WEEKEND);
}
}
# Output
Initializing enum with [Monday, Tuesday, Wednesday, Thursday, Friday]
Initializing enum with [Saturday, Sunday]
EnumSingleton{days=[Monday, Tuesday, Wednesday, Thursday, Friday]}
EnumSingleton{days=[Saturday, Sunday]}