Singleton Design Pattern

  • 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 calling getInstance() 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;
      	}
      }
  • 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.

Various ways to make singleton design pattern implemented using lazy initialization , thread safe

  • Make method synchronized and variable instance volatile
    class 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 instance volatile
    class 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 if instance==null or not. but let say if instance is not null, then also it acquire lock and checking instance==null , as we know acquiring lock every time may increase overhead and create slowness. so to avoid it we can use double check
      class 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;
      	}
      }

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 clone method, to enable cloning for instance of a class we need to implement Cloneable interface , it is marker interface and do not have any method, along with it we need to override clone method.
    • if we don’t override clone method and if try to call clone we will get `'clone()' has protected access in 'java.lang.Object'` error. it means clone() method in the Object class is protected, 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 CloneNotSupportedException exception from clone method.
      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();
          }
      }

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");
        }
    }

Comments

  1. Singleton Design pattern are often used for logging, driver objects, caching, thread pool, database connections, and more.

    ReplyDelete

Post a Comment