- Here we are discussing Singleton Design Pattern in Java, so we are using words/terminology accordingly.
- Singleton Design Pattern helps to restrict only one instance of a class throughout entire application. it means that it creates a new instance of the class if no instance exists, if an instance already exists, it simply returns a reference to that object.
- It comes under creational design pattern.
How can we create class singleton ?
- Make constructor of class as private. it helps to not create object of class using new keyword.
- Create a private static class variable of same type as class, so that it can hold the instance of class.
- create public static method that will return the instance of the class. it act as global access point, to access the class instance.
- code will look like as below
class MySingletonClass{ private static MySingletonClass instance; private MySingletonClass(){} public static MySingletonClass getInstance(){ if(instance==null){ instance = new MySingletonClass(); } return instance; } }
Whatever we discussed so far this is one way to implement singleton design pattern on a class, there are other ways as well. lets understand one by one.
- Eager Initialization
- lets see below code
class MySingletonClass{ private static MySingletonClass instance = new MySingletonClass(); private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } }
- Here whenever class loads into memory, an instance of it creates and referred by variable
instance, even before callinggetInstance()method.
- As it creating instance at the time of class loading, so it ensures only one single instance in multi-thread environment.
- Similar to Eager initialization, we can initialize instance using static block.
class MySingletonClass{ private static MySingletonClass instance; static{ instance = new MySingletonClass(); } private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } }
- lets see below code
- Lazy Initialization
- lets see below code
class MySingletonClass{ private static MySingletonClass instance; private MySingletonClass(){} public static MySingletonClass getInstance(){ if(instance==null){ instance = new MySingletonClass(); } return instance; } }
- here we are creating instance whenever
getInstance()method calls first time (instance==null) else it returning the same instance.
- in multi-thread environment, multiple thread may create multiple instance of class and hence it violates singleton design pattern.
- lets see below code
Various ways to make singleton design pattern implemented using lazy initialization , thread safe
- Make method synchronized and variable
instancevolatileclass MySingletonClass{ private static volatile MySingletonClass instance; private MySingletonClass(){} public static synchronized MySingletonClass getInstance(){ if(instance==null){ instance = new MySingletonClass(); } return instance; } }
- Use synchronized block and variable
instancevolatileclass MySingletonClass{ private static volatile MySingletonClass instance; private MySingletonClass(){} public static MySingletonClass getInstance(){ synchronized(MySingletonClass.class){ if(instance==null){ return instance = new MySingletonClass(); } } return instance; } }- One issue here, Whenever
getInstance()method calls it first acquire locks and then check ifinstance==nullor not. but let say ifinstanceis not null, then also it acquire lock and checkinginstance==null, as we know acquiring lock every time may increase overhead and create slowness. so to avoid it we can use double checkclass MySingletonClass{ private static volatile MySingletonClass instance; private MySingletonClass(){} public static MySingletonClass getInstance(){ if(instance==null){ synchronized(MySingletonClass.class){ if(instance==null){ return instance = new MySingletonClass(); } } } return instance; } }
- One issue here, Whenever
Bill Pugh Singleton
There is a way to implement singleton design pattern using nested class, it is called Bill Pugh Singleton implementation.
- It similar to lazy implementation.
- It initialize instance on demand, this is because nested classes are not loaded until they are referenced.
- It also guarantees thread safety without synchronization.
import java.io.*;
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) {
MySingletonClass instance1 = MySingletonClass.getInstance();
MySingletonClass instance2 = MySingletonClass.getInstance();
System.out.println(instance1==instance2); // true
}
}
class MySingletonClass{
private MySingletonClass(){
}
private static class NestedClass{
private static final MySingletonClass instance = new MySingletonClass();
}
public static MySingletonClass getInstance(){
return NestedClass.instance;
}
}
So far we understood various ways to implement singleton design pattern, and whatever approaches we discussed, we made it thread safe for multi-thread environment, but still we have some issues with it.
- Serialization and Deserialization of singleton class.
- lets say we have serialized singleton class, during deserialization it can create multiple instance of it which will break singleton design pattern.
- let understand this issue with the help of code.
import java.io.*; public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException { MySingletonClass instance1 = MySingletonClass.getInstance(); MySingletonClass instance2 = MySingletonClass.getInstance(); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file.single")); out.writeObject(instance1); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.single")); MySingletonClass instance3 = (MySingletonClass)in.readObject(); in.close(); in = new ObjectInputStream(new FileInputStream("file.single")); MySingletonClass instance4 = (MySingletonClass)in.readObject(); in.close(); System.out.println(instance1==instance2); // true System.out.println(instance2==instance3); // false System.out.println(instance1==instance3); // false System.out.println(instance1==instance4); // false System.out.println(instance3==instance4); // false System.out.println(instance2==instance4); // false } } class MySingletonClass implements Serializable { private static MySingletonClass instance = new MySingletonClass(); private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } }
- To fix it we can write method
readResolve()in our singleton class as shown below.class MySingletonClass implements Serializable { private static MySingletonClass instance = new MySingletonClass(); private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } protected Object readResolve(){ return instance; } }
- Cloning of Singleton class
- first let’s understand about
clonemethod, to enable cloning for instance of a class we need to implementCloneableinterface , it is marker interface and do not have any method, along with it we need to overrideclonemethod.
- if we don’t override
clonemethod and if try to callclonewe will get `'clone()' has protected access in 'java.lang.Object'` error. it meansclone()method in theObjectclass isprotected, it can only be called from within the class itself or from a subclass.
- Now as we read that we have to override clone then only we can use it. so its better to not override it in singleton class and we will never be in situation where because of cloning, singleton design pattern break.
- But sometime it happens that Singleton class extends another class which override clone method and because of which it comes into situation where cloning will break singleton design pattern. let see it in code.
import java.io.*; public class Main { public static void main(String[] args) throws CloneNotSupportedException { MySingletonClass instance1 = MySingletonClass.getInstance(); MySingletonClass instance2 = (MySingletonClass) instance1.clone(); System.out.print(instance1==instance2); // false } } class MyClass implements Cloneable{ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class MySingletonClass extends MyClass{ private static MySingletonClass instance = new MySingletonClass(); private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } }
- To fix it we can throw
CloneNotSupportedExceptionexception fromclonemethod.class MySingletonClass extends MyClass implements Cloneable{ private static MySingletonClass instance = new MySingletonClass(); private MySingletonClass(){} public static MySingletonClass getInstance(){ return instance; } @Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }
- first let’s understand about
Break singleton design pattern using reflection API
let’s see below code
- here we are accessing singleton class constructor and making it accessible to create new instance using reflection API.
import java.io.*;
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) {
MySingletonClass instance1 = MySingletonClass.getInstance();
MySingletonClass instance2 = null;
try{
Constructor[] constructors = MySingletonClass.class.getDeclaredConstructors();
for(Constructor constructor : constructors){
constructor.setAccessible(true);
instance2 = (MySingletonClass) constructor.newInstance();
break;
}
}
catch (Exception e){
e.printStackTrace();
}
System.out.println(instance2==null); // false
System.out.println(instance1==instance2); // false
}
}
class MySingletonClass{
private static MySingletonClass instance = new MySingletonClass();
private MySingletonClass(){}
public static MySingletonClass getInstance(){
return instance;
}
}Below is the code to fix it,
- Here in singleton class constructor we can check if instance is not null then we can throw an exception to prevent reflection API creating new instance of it.
import java.io.*;
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) {
MySingletonClass instance1 = MySingletonClass.getInstance();
MySingletonClass instance2 = null;
try{
Constructor[] constructors = MySingletonClass.class.getDeclaredConstructors();
for(Constructor constructor : constructors){
constructor.setAccessible(true);
instance2 = (MySingletonClass) constructor.newInstance();
break;
}
}
catch (Exception e){
e.printStackTrace();
/*
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.coderstuff01.Main.main(Main.java:17)
Caused by: java.lang.RuntimeException: Use getInstance() method to get instance of this class
at com.coderstuff01.MySingletonClass.<init>(Main.java:46)
... 5 more
*/
}
System.out.println(instance2==null); // true
System.out.println(instance1==instance2); // false
}
}
class MySingletonClass{
private static MySingletonClass instance = new MySingletonClass();
private MySingletonClass(){
if(instance!=null){
throw new RuntimeException("Use getInstance() method to get instance of this class");
}
}
public static MySingletonClass getInstance(){
return instance;
}
}
Singleton design pattern using Enum
Let’s discuss one of the best way to implement singleton design pattern,
- It stand strong in multi-thread environment.
- Provide serialization safety.
- Provide clone safety.
- Even reflection can not break it.
let see code
public class Main { public static void main(String[] args) { SingletonEnum instance1 = SingletonEnum.INSTANCE; SingletonEnum instance2 = SingletonEnum.INSTANCE; System.out.println(instance1==instance2); // true } } enum SingletonEnum{ INSTANCE; public void performSomething(){ System.out.println("Hello"); } }
Singleton Design pattern are often used for logging, driver objects, caching, thread pool, database connections, and more.
ReplyDelete