Singleton Design Pattern
Singleton pattern is one of the simplest but most controversial design pattern in the Creational Design Pattern category. It comes from the Gangs of Four Design patterns Group. Using the Singleton pattern developers can restrict the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine.
It ensure a class only has one instance, and provide a global point of access to it. - GoF
There are many ways to implement singleton design patterns but all have these common steps.
- Private constructor to restrict instantiation of the class from other classes.
- The private static variable of the same class is the only instance of the class.
- Public static method that returns the instance of the class, this is the global access point for the outer world to get the instance of the singleton class.
Let's try to implement the singleton design pattern in different ways.
Eager Initialization
In eager initialization, the instance of Singleton Class is created at the time of class loading.
Here is the implementation of the static initialization singleton class.
package com.geekscoder.singleton;
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
Problem In Eager Initialization
This is the easiest method to create a singleton class but it has a drawback that instance is created even though the client application might not be using it and there is no exception handling in this code. hence it will not be the right approach to implement a singleton design pattern. however, exception handling can be achieved using static block as explained in the below code.
package com.geekscoder.singleton;
public class Singleton {
private static Singleton instance;
private Singleton(){}
static{
try{
instance = new Singleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static Singleton getInstance(){
return instance;
}
}
Lazy Initialization
The lazy initialization method to implement the Singleton pattern creates the instance in the global access method. code for creating a singleton class is given below.
package com.geekscoder.singleton;
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
Problem In Lazy Initialization
The above code will work in a single thread environment but what if two threads will access the if block at the same time?
It will destroy the singleton pattern and both threads will get the different instances of the singleton class. To fix this issue we need to create a thread-safe singleton class. how we will do that?
The easier 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. General implementation of this approach is like the below class.
package com.geekscoder.singleton;
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
making the getInstance() method synchronized will solve the issue but it will create performance issues for the application using this code. to overcome this issue we need to implement double-checked locking principle here. let see how we can do it in the below code.
package com.geekscoder.singleton;
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
Bill Pugh Singleton Implementation
Prior to Java 5, the java memory model had a lot of issues and the above approaches used to fail in certain scenarios where too many threads try to get the instance of the Singleton class simultaneously. So Bill Pugh came up with a different approach to create the Singleton class using an inner static helper class. The Bill Pugh Singleton implementation goes like this;
package com.geekscoder.singleton;
public class Singleton {
private Singleton(){}
private static class SingletonHelper{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
Notice the private inner static class that contains the instance of the singleton class. When the singleton class is loaded, SingletonHelper class is not loaded into memory and only when someone calls the getInstance method, this class gets loaded and creates the Singleton class instance. This is the most widely used approach for the Singleton class as it doesn’t require synchronization.
How Reflection can destroy Singleton Patterns
Reflection can be used to destroy all the above singleton implementation approaches. Let’s see this with an example class.
package com.geekscoder.singleton;
import java.lang.reflect.Constructor;
public class SingletonTest {
public static void main(String[] args) {
Singleton instanceOne = Singleton.getInstance();
Singleton instanceTwo = null;
try {
Constructor[] constructors = Singleton.class.getDeclaredConstructors();
for (Constructor constructor : constructors) {
constructor.setAccessible(true);
instanceTwo = (Singleton) constructor.newInstance();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(instanceOne.hashCode());
System.out.println(instanceTwo.hashCode());
}
}
When you run the above test class, you will notice that hashCode of both the instances is not the same that destroys the singleton pattern.
Making Singleton With Enum
Enums are inherently serializable, we don't need to implement them with a serializable interface. The reflection problem is also not there. Therefore, it is 100% guaranteed that only one instance of the singleton is present within a JVM. Thus, this method is recommended as the best method of making singletons in Java. Let's try to implement it in code.
public enum SingletonEnum {
INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
public class EnumDemo {
public static void main(String[] args) {
SingletonEnum singleton = SingletonEnum.INSTANCE;
System.out.println(singleton.getValue());
singleton.setValue(2);
System.out.println(singleton.getValue());
}
}
One thing to remember here is, when serializing an enum, field variables are not getting serialized. For example, if we serialize and deserialize the SingletonEnum
class, we will lose the value of the int value
field (Refer to the Oracle docs for more details about enum serialization).
Serialization and Singleton
In order to serialize the singleton classes, we implement the Serializable interface. But doing that is not enough. When deserializing a class, new instances are created. Now it doesn't matter the constructor is private or not. Now there can be more than one instance of the same singleton class inside the JVM, violating the singleton property.
public class SingletonWithSerialization implements Serializable {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
singleton.setValue(1);
// Serialize
try {
FileOutputStream fileOut = new FileOutputStream("out.serialization");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(singleton);
out.close();
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
singleton.setValue(2);
// Deserialize
Singleton singleton2 = null;
try {
FileInputStream fileIn = new FileInputStream("out.serialization");
ObjectInputStream in = new ObjectInputStream(fileIn);
singleton2 = (Singleton) in.readObject();
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
} catch (ClassNotFoundException c) {
System.out.println("singletons.SingletonEnum class not found");
c.printStackTrace();
}
if (singleton == singleton2) {
System.out.println("both objects are same");
} else {
System.out.println("both objects are not same");
}
System.out.println(singleton.getValue());
System.out.println(singleton2.getValue());
}
}
So it destroys the singleton pattern, to overcome this scenario all we need to do it provide the implementation of the readResolve() method.
protected Object readResolve() {
return getInstance();
}
After overriding this method both objects will be the same.