Saturday, December 1, 2012

Singleton

Why Singleton ?
Some objects are heavy to instantiate and meant to be instantiated only once throughout the application lifetime.
Some objects like database connection, window frame for GUI application, Network sockets etc.
That's what we need sometimes need to write programs keeping such situations in mind.

What is Singleton ?
Before the concepts of computer, Singleton meant a set with exactly one element in it. You can get the details here.
In Computer Science and IT Industry, Singleton is a creational  Design Pattern. This basically gives you specialized suggestions while writing a singleton implementation.

OK, How to do that ?
We need to create only one object of this class. So, we need to how we can create objects. There are basically four ways, we can create an object of a class.

  1. Using new keyword
    The most common way to create object.
     MyObject object = new MyObject();
  2. Using clone()
    MyObject anotherObject = new MyObject();
    MyObject object = anotherObject.clone();
  3. Using Class.forName()
    MyObject object = (MyObject) Class.forName("subin.rnd.MyObject").newInstance();
  4. Using object deserialization
    ObjectInputStream inStream = new ObjectInputStream(anInputStream );
    MyObject object = (MyObject) inStream.readObject();
So, we know we can create objects in these ways. Now we have to restrict all these possibilities.

Hey, if you restrict all of them, then how the one object will be created ?
Well, we will do it in the class itself. I think you are aware of static methods.

OK, carry on...

Apart from the object creation techniques, we also need to take care of multi-threading as well. Cause, it can also create two objects at the same time.

OK, not much of theory at this point, let's get our hand dirty.

 package main;  
 import java.io.FileInputStream;  
 import java.io.FileOutputStream;  
 import java.io.IOException;  
 import java.io.ObjectInputStream;  
 import java.io.ObjectOutputStream;  
 import java.io.Serializable;  
 /**  
  * Singleton utility  
  *   
  * @author palash.k  
  *   
  */  
 class Singleton implements Cloneable, Serializable {  
      /**  
       *   
       */  
      private static final long serialVersionUID = 1L;  
      /*  
       * Private variable to hold the instance of the Singleton object  
       */  
      private static Singleton instance;  
      /**  
       * Private constructor to block instantiation using new and Class.forName()  
       */  
      private Singleton() {  
           // Dummy constructor  
      }  
      /**  
       * Instantiates this class if object is not available and returns the  
       * instance  
       *   
       * @return the singleton instance  
       */  
      public static Singleton getInstance() {  
           if (instance == null) {  
                synchronized (Singleton.class) {  
                     // Double checked thread locking  
                     if (instance == null) {  
                          instance = new Singleton();  
                     }  
                }  
           }  
           return instance;  
      }  
      /*  
       * (non-Javadoc)  
       *   
       * @see java.lang.Object#clone()  
       */  
      protected Object clone() throws CloneNotSupportedException {  
           throw new CloneNotSupportedException("Singleton class cannot be cloned");  
      }  
      /**  
       * This also returns the instance of the single ton class. Which ensures  
       * singleton behavior through deserialization.  
       *   
       * @return the instance of Singleton  
       */  
      public final Object readObject() {  
           return getInstance();  
      }  
 }  
 /**  
  * Class to get Singleton instance  
  *   
  * @author palash.k  
  *   
  */  
 class TrySingletonThread implements Runnable {  
      /*  
       * (non-Javadoc)  
       *   
       * @see java.lang.Runnable#run()  
       */  
      @Override  
      public void run() {  
           System.out.println(Thread.currentThread().getName()  
                     + " got singleton with ID: "  
                     + Singleton.getInstance().hashCode());  
      }  
 }  
 /**  
  * Singleton test  
  *   
  * @author palash.k  
  *   
  */  
 public class Main {  
      public static void main(String args[]) throws IOException {  
           // We'll try all possibilities. We can not use new keyword here. Trying  
           // others.  
           // Try Class.forName()  
           int i = 0;  
           try {  
                @SuppressWarnings("unused")  
                Singleton singleton = (Singleton) Class.forName("main.Singleton")  
                          .newInstance();  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
           Singleton singleton = Singleton.getInstance();  
           FileOutputStream fileOut = new FileOutputStream("singleton.ser");  
           ObjectOutputStream out = new ObjectOutputStream(fileOut);  
           out.writeObject(singleton);  
           out.close();  
           fileOut.close();  
           // Try deserialization  
           try {  
                FileInputStream fileIn = new FileInputStream("singleton.ser");  
                ObjectInputStream in = new ObjectInputStream(fileIn);  
                singleton = (Singleton) in.readObject();  
                in.close();  
                fileIn.close();  
           } catch (IOException ioe) {  
                ioe.printStackTrace();  
                return;  
           } catch (ClassNotFoundException c) {  
                c.printStackTrace();  
                return;  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
           // Try clone  
           try {  
                @SuppressWarnings("unused")  
                Singleton singleton2 = (Singleton) singleton.clone();  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
           // Try multi-threading  
           while (i < 10) {  
                Thread t = new Thread(new TrySingletonThread());  
                t.setName("Thread - " + i);  
                t.start();  
                i++;  
           }  
      }  
 }  
Put any value in the while loop, you will get only one instance of class Singleton.

I recently got a nice solution to performance bottleneck using this pattern. Server was crashed due to unnecessary instantiation of objects in our code. We used this pattern and server recovered from the crashing issue.


Prev
Palash Kanti Kundu

2 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Double check is incorrectly implemented. The static field "instance" MUST be declared volatile. Better use holder class idiom for Singletons.

    ReplyDelete