Thursday, December 1, 2011

Singleton and serialization

What is singleton?

In Java ,we can create objects by calling constructor.But imagine a scenario where we want to 
control object instantiation.There could be many reasons why we want to control the object creation.
Normally in 3 tier architecture we create single instance of service and DAO objects since we don't want to create multiple DAO objects as number of database connections are limited and by creating multiple DAO objectswe donot want to exhaust database connections.
This is just one example ,there could be multiple such examples in real world.

Code snippet for a singleton class

Here I am using double check mechanism for creating a singleton instance.

package com.kunaal.algo;

import java.io.Serializable;

/**
 * Here we are making ConnectionFactory as a singleton.
 * Since we want connection factory to be initiated once and used
 * by different classes of the project.
 * 
 * We also want to read the connection parameters once and use it as
 * a place holder for pooled connections.
 * 
 * @author KunaalATrehan
 *
 */
public class ConnectionFactory implements Serializable{
 //Static variable for holding singleton reference object
 private static ConnectionFactory INSTANCE;
 
 /**
  * Private constructor
  */
 private ConnectionFactory(){  
 }
 
 /**
  * Static method for fetching the instance
  * @return
  */
 public static ConnectionFactory getInstance(){
  //Check whether instance is null or not
  if(INSTANCE ==null){
   //Locking the class object
   synchronized(ConnectionFactory.class){
    //Doing double check for the instance
    //This is required in case first time two threads simultaneously invoke 
    //getInstance().So when another thread get the lock,it should not create the 
    //object again as its already created by the previous thread.
    if(INSTANCE==null)
     INSTANCE=new ConnectionFactory();
   }   
  }
  
  return INSTANCE;
 }
}

What happens when we serialize the singleton?

Serialization allows storing the object in some data store and re create it later on.However when we serialize a singleton class and invoke deserialization multiple times.We can end up with multiple objects of the singleton class.Even though constructor is private,deserialization process gets hold of the private constructor while recreating the object from the serialized data store.

So can we avoid it.Yes we can avoid it.We will go through step by step and explain what needs to be done when we reconstruct the object from the serialized data store so that singleton behavior is not broken when object reconstruction happens.

Case-1: Serialization breaking singleton behavior

Here we are serializing the singleton instance and reading it multiple times.So we will see that INSTANCE reference is same,however multiple objects are created.


package com.kunaal.algo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * @author KunaalATrehan
 *
 */
public class SerializationTest {

 /**
  * @param args
  * @throws IOException 
  * @throws FileNotFoundException 
  * @throws ClassNotFoundException 
  */
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ConnectionFactory INSTANCE=ConnectionFactory.getInstance();
  
  //Here I am serializing the connection factory instance
  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser"));  
        oos.writeObject(INSTANCE);  
        oos.close();  
        
        //Here I am recreating the instance by reading the serialized object data store
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
        ConnectionFactory factory1 = (ConnectionFactory) ois.readObject();  
        ois.close();  
        
        //I am recreating the instance AGAIN by reading the serialized object data store
        ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
        ConnectionFactory factory2 = (ConnectionFactory) ois2.readObject();  
        ois2.close();
        
        //Lets see how we have broken the singleton behavior
        
        System.out.println("Instance reference check->" +factory1.getInstance());
        System.out.println("Instance reference check->" +factory2.getInstance());
        System.out.println("=========================================================");
        System.out.println("Object reference check->"+factory1);
        System.out.println("Object reference check->"+factory2);
 }

}

Output is as follows:-


Instance reference check->com.kunaal.algo.ConnectionFactory@763f5d
Instance reference check->com.kunaal.algo.ConnectionFactory@763f5d
=========================================================
Object reference check->com.kunaal.algo.ConnectionFactory@13a317a
Object reference check->com.kunaal.algo.ConnectionFactory@186768e

So it has created two objects and one static reference for INSTANCE.So that means if we read the serialized format of singleton object multiple times,we will create multiple objects.This is not what singleton object is supposed to do.So can we avoid it,Yes we can.We will discuss the same in the next use case.

Case-2: Serialization and singleton working properly

In order to make serialization and singleton work properly,we have to introduce readResolve() method in the singleton class.readResolve() method lets developer control what object should be returned  on deserialization.
For the current ConnectionFactory singleton class,readResolve() method will look like this.
       /**
  * Special hook provided by serialization where developer can control what object needs to sent.
  * However this method is invoked on the new object instance created by de serialization process.
  * @return
  * @throws ObjectStreamException
  */
 private Object readResolve() throws ObjectStreamException{
  return INSTANCE;
 }

Output  is as follows:-

Instance reference check->com.kunaal.algo.ConnectionFactory@13a317a
Instance reference check->com.kunaal.algo.ConnectionFactory@13a317a
=========================================================
Object reference check->com.kunaal.algo.ConnectionFactory@13a317a
Object reference check->com.kunaal.algo.ConnectionFactory@13a317a

So now serialization and singleton is working properly and it does not matter how many times we read the serialized format of singleton object.We will get the one instance.readResolve() did the trick

22 comments:

  1. Great post. Solved my time

    ReplyDelete
  2. Great! Thanks for sharing. Can you please elaborate about readResolve method? Thanks in advance.

    ReplyDelete
  3. thanks.... really very helpful...

    ReplyDelete
  4. Super Simple and Understandable....Thanks~~

    ReplyDelete
  5. Super step by step flow

    ReplyDelete
  6. Great ! Thanks !. Solved my problem.

    ReplyDelete
  7. Nice post ....
    In case we have different source and destination JVM and file is transported over network ,
    It should be something like

    private Object readResolve() throws ObjectStreamException{
    return getInstance();
    }

    ReplyDelete
  8. really helpful post solved many more .but please can u explain more about getResolve()

    ReplyDelete
  9. how to invoke readResolve() at the time of deserialization

    ReplyDelete
  10. Super And Clear

    Thanks for clear Explanation

    ReplyDelete
  11. very nice and clear.. rare resource in net .. thanks!

    ReplyDelete
  12. Thank you, Helped at Javarush(lvl20)

    ReplyDelete
  13. I have read your blog its very attractive and impressive. I like it your blog.

    Java Online Training Java EE Online Training Java EE Online Training Java 8 online training Java 8 online training

    Java Online Training from India Java Online Training from India Core Java Training Online Core Java Training Online Java Training InstitutesJava Training Institutes

    ReplyDelete
  14. Now I understand where this task on Javarush ))

    ReplyDelete
  15. Awesome explanation.Too easy to understand the concept.

    ReplyDelete

  16. What if i serialize it one JVM and export to other JVM?

    ReplyDelete
  17. There is a comment in Javarush.ru(platform to learn Java), which referes to this article. It has 12 likes.
    Also, another 3 user mentioned this article, too.
    So, your article really helps pupils to resolve one problem from the study course.
    Thank you very much!

    P.s.Sorry for my English

    ReplyDelete