03 July 2017

Singleton Design Pattern in Java

Singleton Pattern:
This is useful in an application where we have to use just one instance of the class and at any point of time more than one instance cannot be created for a class.
How to create Singleton design pattern?
To create the singleton class, we need to have static member of class, private constructor and static factory method.
Static member: It gets memory only once because of static, it contains the instance of the Singleton class.
Private constructor: It will prevent to instantiate the Singleton class from outside the class.
Static factory method: This provides the global point of access to the Singleton object and returns the instance to the caller.
Code Example 1:
package singleton;
public class Logger
{
private static Logger obj = new Logger();
private Logger() {}
public static Logger getLoggerObject()
{
return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

package singleton;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton");
      
}
}
DisAdvantage: Object created before its use.
Code Example 2: Lazy instantiation of singleton design pattern
//Code Example 2: Lazy instantiation of singleton design pattern
package singleton2;

public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              obj = new Logger();
return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

package singleton2;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization");
      
}
}
DisAdvantage: Not thread safe.
Code Example3 : Thread safe Lazy instantiation of singleton design pattern
// Code Example3 : Thread safe Lazy instantiation of singleton design pattern
package singleton3;

public class Logger {
       private static Logger obj;
       private Logger() {}
       public static synchronized Logger getLoggerObject()
       {
              if(obj == null)
                     obj = new Logger();
       return obj;
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }
}
// client calling singleton class
package singleton3;

public class SingletonExample {
       public static void main(String[] args)
       {

       Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization and thread safe");
             
       }

}
DisAdvantage: Not performant. Above implementation works fine and provides thread-safety but it reduces the performance because of cost associated with the synchronized method. The object creation need to be synchronized only for first few threads.



Code Example 4: Thread safe cum performant singleton with double checked locking principle
// Code Example 4: Thread safe cum performant singleton with double checked locking principle
package singleton4;

public class Logger
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              synchronized(Logger.class)
              {
                     if(obj == null)
                     {
                     obj = new Logger();
                     }
                                        
              }
       return obj;
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}
package singleton4;

import singleton4.Logger;

public class SingletonExample
{
public static void main(String[] args)
{

Logger.getLoggerObject().performLogging("Test Singleton with lazy initialization with thread safe and performant");
      
}
}
Code Example 5: Avoiding breaking singleton pattern using deserialization of object
If singleton class is serializable, you can serialize singleton class object to a file and deserialize it to get one more instance of the same class, to avoid this override readResolve() method which will be called just after the deserialization. In this method return singleton object as below code example:
package singleton5;

import java.io.Serializable;

public class Logger implements Serializable {
       private static Logger obj;
       private Logger() {}
       public static Logger getLoggerObject()
       {
              if(obj == null)
                     synchronized(Logger.class)
                     {
                           if(obj == null)
                           {
                           obj = new Logger();
                           }
                                               
                     }
              return obj;
       }
       protected Object readResolve()
       {
              System.out.println("in readResolve after deserialization");
              return getLoggerObject();
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }

}


package singleton5;

import java.io.*;

public class SingletonExample {
       public static void main(String[] args) throws ClassNotFoundException
       {
        Logger test1,test2;
     test1 = Logger.getLoggerObject();
        test1.performLogging("Test Singleton with lazy initialization with thread safe and performant");      
       // calling singleton class with deserialization
       try {
              ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Logger.ser"));
              out.writeObject(test1);
              out.close();
             
              ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logger.ser"));
              test2 =(Logger) in.readObject();
              in.close();
              test2.performLogging(" Testing with deserialization");
              System.out.println("Hashcode of instance with name test1:"+test1.hashCode());
              System.out.println("Hashcode of instance with name test2:"+test2.hashCode());
       } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       }
             
       }
}

Code Example 6: Using reflection to break singleton.
Using reflection we can break singleton design pattern, review the below code for example. Here we read the constructors of singleton class using reflection and create new instance to the singleton class which is different from actual object returned by getLoggerObject() method
package singleton6;

import java.io.Serializable;

public class Logger implements Serializable {
       private static Logger obj;
       private Logger() {}
       public static Logger getLoggerObject()
       {
              if(obj == null)
                     synchronized(Logger.class)
                     {
                           if(obj == null)
                           {
                           obj = new Logger();
                           }
                                               
                     }
              return obj;
       }
       protected Object readResolve()
       {
              System.out.println("in readResolve after deserialization");
              return getLoggerObject();
       }
       public void performLogging( String x)
       {
              // write into console
              System.out.println("Error "+x);

       }

}


package singleton6;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonExample {
public static void main(String[] args)
{
       Logger obj1 = Logger.getLoggerObject();
       obj1.performLogging("logging using obj1");
       System.out.println("Hashcode of obj1:"+obj1.hashCode());
       // breaking singleton using reflection
       Logger obj2 = null;
       try {
              Constructor[] cons = Logger.class.getDeclaredConstructors();
              for(Constructor constructor:cons)
              {
                     constructor.setAccessible(true);
                     obj2 = (Logger) constructor.newInstance();
                     obj2.performLogging("logging using obj2");
                     System.out.println("Hashcode of obj2:"+obj2.hashCode());
              }
       } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
       }
      
}
}

Overcome reflection issue: To overcome issue raised by reflection, enums are used because java ensures internally that enum value is instantiated only once. Since java Enums are globally accessible, they can be used for singletons. Its only drawback is that it is not flexible i.e it does not allow lazy initialization.

//Java program for Enum type singleton
public enum Logger
{
  INSTANCE;
}
Code Example 7: Breaking Singleton using Cloning and how to avoid it
Java has the ability to create a copy of object with similar attributes and state form original object. This concept in java is called cloning. To implement cloning, we have to implement java.lang.Cloneable interface and override clone() method from Object class. It is a good idea to prevent cloning in a singleton class. To prevent cloning on singleton object, let us explicitly throw CloneNotSupportedException exception in clone() method.
// Code Example 7: Breaking Singleton using Cloning and how to avoid it
package singleton7;

import singleton7.Logger;

public class Logger implements Cloneable
{
private static Logger obj;
private Logger() {}
public static Logger getLoggerObject()
{
       if(obj == null)
              synchronized(Logger.class)
              {
                     if(obj == null)
                     {
                     obj = new Logger();
                     }
                                        
              }
       return obj;
}
public Object clone() throws CloneNotSupportedException
{
       throw new CloneNotSupportedException();
      
}
public void performLogging( String x)
{
       // write into console
       System.out.println("Error "+x);

}
}

//Code Example 7: Breaking Singleton using Cloning and how to avoid it
package singleton7;

public class SingletonExample {
       public static void main(String[] args)
       {
Logger obj1 = Logger.getLoggerObject();
obj1.performLogging("logging using obj1");
System.out.println("Hashcode of obj1:"+obj1.hashCode());
              try
              {
              Logger obj2 = (Logger)obj1.clone();
              }
              catch (CloneNotSupportedException e)
              {
       // TODO Auto-generated catch block
              System.out.println("clone failed due to override of clone method"+e);
              }
       }
}

Here above we have stopped user to create clone of singleton class. If you dont want to throw exception you can also return the same instance of class from clone method.
Code Example 8 within JDK:
getRuntime() method of java.lang.Runtime
The java.lang.Runtime class allows the application to interface with the environment in which the application is running. The java.lang.Runtime.getRuntime() method returns the runtime object associated with the current Java application which implements singleton pattern. Most of the methods of class Runtime are instance methods and must be invoked with respect to the current runtime object.
For example availableProcessors() method returns the number of processors available to the Java virtual machine. freeMemory() method of Runtime class returns the amount of free memory in the Java Virtual Machine.
package singleton8;

public class SingletonExample {

          public static void main(String[] args) {

             // print when the program starts
             System.out.println("Program starting...");

             // get the current runtime singleton object associated with this process
             Runtime run1 = Runtime.getRuntime();
             Runtime run2 = Runtime.getRuntime();
             System.out.println("Hashcode of two objects of java.lang.Runtime class:: \nrun1:"+run1.hashCode()+"\nrun2:"+run2.hashCode());

             // print the current free memory for this runtime
             System.out.println("JVM free memory:" + run1.freeMemory());
             System.out.println("JVM available processors:" + run1.availableProcessors());
             System.out.println("JVM Max Memory:" + run1.maxMemory());
            
          }
       }

Note: If singleton class is loaded by two class loaders then we can create two instances of same singleton class will be created, one for each class loader.

Factory Method Design Pattern
We have super class and N number of sub classes, and based on the input data provided we need to return object of one of the sub-classes, here we use Factory method pattern.
Example within JDK: java.util.Calendar # getInstance()

No comments:

Post a Comment