Singleton design pattern

The singleton pattern is a design pattern that restricts the instantiation of a class to one object. 

Sometimes we need to have only one instance of our class for example a single DB connection shared by multiple objects as creating a separate DB connection for every object may be costly. Similarly, there can be a single configuration manager or error manager in an application that handles all problems instead of creating multiple managers.

Examples of Singleton class

  1. java.lang.Runtime : Java provides a class Runtime in its lang package which is singleton in nature. Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime() method.
    An application cannot instantiate this class so multiple objects can’t be created for this class. Hence Runtime is a singleton class.

Advantage of Singleton design pattern

  • Saves memory because object is not created at each request. Only single instance is reused again and again.

Usage of Singleton design pattern


  • Singleton pattern is mostly used in multi-threaded and database applications. It is used in logging, caching, thread pools, configuration settings etc.
  • Hardware interface access: The use of singleton depends on the requirements. Singleton classes are also used to prevent concurrent access of class. Practically singleton can be used in case external hardware resource usage limitation required e.g. Hardware printers where the print spooler can be made a singleton to avoid multiple concurrent accesses and creating deadlock.
  • Logger : Singleton classes are used in log file generations. Log files are created by logger class object. Suppose an application where the logging utility has to produce one log file based on the messages received from the users. If there is multiple client application using this logging utility class they might create multiple instances of this class and it can potentially cause issues during concurrent access to the same logger file. We can use the logger utility class as a singleton and provide a global point of reference, so that each user can use this utility and no 2 users access it at same time.
  • Configuration File: This is another potential candidate for Singleton pattern because this has a performance benefit as it prevents multiple users to repeatedly access and read the configuration file or properties file. It creates a single instance of the configuration file which can be accessed by multiple calls concurrently as it will provide static config data loaded into in-memory objects. The application only reads from the configuration file at the first time and there after from second call onward the client applications read the data from in-memory objects.
  • Cache: We can use the cache as a singleton object as it can have a global point of reference and for all future calls to the cache object the client application will use the in-memory object.

Significance of Classloader in Singleton Pattern

If singleton class is loaded by two classloaders, two instance of singleton class will be created, one for each classloader.


Lazy loading example
// Double Checked Locking based Java implementation of
// singleton design pattern
class Singleton {
    private volatile static Singleton obj;
  
    private Singleton() {}
  
    public static Singleton getInstance()     {
        if (obj == null)         {
            // To make thread safe
            synchronized (Singleton.class)             {
                // check again as multiple threads
                // can reach above step
                if (obj==null)
                    obj = new Singleton();
            }
        }
        return obj;
    }
}



How to prevent Singleton Pattern from Reflection, Serialization and Cloning?

There are mainly 3 concepts which can break singleton property of a class. Let’s discuss them one by one.
  1. Reflection: Reflection can be caused to destroy singleton property of singleton class, as shown in following example:



    // Java code to explain effect of Reflection
    // on Singleton property
      
    import java.lang.reflect.Constructor;
      
    // Singleton class
    class Singleton  {
        // public instance initialized when loading the class
        public static Singleton instance = new Singleton();
          
        private Singleton()      {
            // private constructor
        }
    }
      
    public class GFG  {
      
        public static void main(String[] args)     {
            Singleton instance1 = Singleton.instance;
            Singleton instance2 = null;
            try        {
                Constructor[] constructors = 
                        Singleton.class.getDeclaredConstructors();
                for (Constructor constructor : constructors)              {
                    // Below code will destroy the singleton pattern
                    constructor.setAccessible(true);
                    instance2 = (Singleton) constructor.newInstance();
                    break;
                }
            }
          
            catch (Exception e)          {
                e.printStackTrace();
            }
              
        System.out.println("instance1.hashCode():- " 
                                          + instance1.hashCode());
        System.out.println("instance2.hashCode():- " 
                                          + instance2.hashCode());
        }
    }
    Output:-
    instance1.hashCode():- 366712642
    instance2.hashCode():- 1829164700
    
    After running this class, you will see that hashCodes are different that means, 2 objects of same class are created and singleton pattern has been destroyed.
    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 GFG  {
      INSTANCE;
    }
    As enums don’t have any constructor so it is not possible for Reflection to utilize it. Enums have their by-default constructor, we can’t invoke them by ourself. JVM handles the creation and invocation of enum constructors internally.As enums don’t give their constructor definition to the program, it is not possible for us to access them by Reflection also. Hence, reflection can’t break singleton property in case of enums.

  2. Serialization:- Serialization can also cause breakage of singleton property of singleton classes. Serialization is used to convert an object of byte stream and save in a file or send over a network. Suppose you serialize an object of a singleton class. Then if you de-serialize that object it will create a new instance and hence break the singleton pattern.



    // Java code to explain effect of 
    // Serilization on singleton classes
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInput;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutput;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
      
    class Singleton implements Serializable  {
        // public instance initialized when loading the class
        public static Singleton instance = new Singleton();
          
        private Singleton()      {
            // private constructor
        }
    }
      
      
    public class GFG  {
      
        public static void main(String[] args) {
            try {
                Singleton instance1 = Singleton.instance;
                ObjectOutput out
                    = new ObjectOutputStream(new FileOutputStream("file.text"));
                out.writeObject(instance1);
                out.close();
          
                // deserailize from file to object
                ObjectInput in 
                    = new ObjectInputStream(new FileInputStream("file.text"));
                  
                Singleton instance2 = (Singleton) in.readObject();
                in.close();
          
                System.out.println("instance1 hashCode:- "
                                                     + instance1.hashCode());
                System.out.println("instance2 hashCode:- " 
                                                     + instance2.hashCode());
            
              
            catch (Exception e)  {
                e.printStackTrace();
            }
        }
    }
    Output:- 
    instance1 hashCode:- 1550089733
    instance2 hashCode:- 865113938
    
    As you can see, hashCode of both instances is different, hence there are 2 objects of a singleton class. Thus, the class is no more singleton.
    Overcome serialization issue:- To overcome this issue, we have to implement method readResolve() method.



    // Java code to remove the effect of 
    // Serialization on singleton classes
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInput;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutput;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
      
    class Singleton implements Serializable  {
        // public instance initialized when loading the class
        public static Singleton instance = new Singleton();
          
        private Singleton()      {
            // private constructor
        }
          
        // implement readResolve method
        protected Object readResolve()     {
            return instance;
        }
    }
      
    public class GFG  {
      
        public static void main(String[] args)      {
            try        {
                Singleton instance1 = Singleton.instance;
                ObjectOutput out 
                    = new ObjectOutputStream(new FileOutputStream("file.text"));
                out.writeObject(instance1);
                out.close();
              
                // deserailize from file to object
                ObjectInput in 
                    = new ObjectInputStream(new FileInputStream("file.text"));
                Singleton instance2 = (Singleton) in.readObject();
                in.close();
              
                System.out.println("instance1 hashCode:- "
                                               + instance1.hashCode());
                System.out.println("instance2 hashCode:- "
                                               + instance2.hashCode());
            
              
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    Output:- 
    instance1 hashCode:- 1550089733
    instance2 hashCode:- 1550089733
    
    Above both hashcodes are same hence no other instance is created.
  3. Cloning: Cloning is a concept to create duplicate objects. Using clone we can create copy of object. Suppose, we ceate clone of a singleton object, then it wil create a copy that is there are two instances of a singleton class, hence the class is no more singleton.



    // JAVA code to explain cloning 
    // issue with singleton
    class SuperClass implements Cloneable {
      int i = 10;
      
      @Override
      protected Object clone() throws CloneNotSupportedException    {
        return super.clone();
      }
    }
      
    // Singleton class
    class Singleton extends SuperClass {
      // public instance initialized when loading the class
      public static Singleton instance = new Singleton();
      
      private Singleton()    {
        // private constructor
      }
    }
      
    public class GFG {
      public static void main(String[] args) throws CloneNotSupportedException {
        Singleton instance1 = Singleton.instance;
        Singleton instance2 = (Singleton) instance1.clone();
        System.out.println("instance1 hashCode:- "
                               + instance1.hashCode());
        System.out.println("instance2 hashCode:- " 
                               + instance2.hashCode()); 
      }
    }
    Output :- 
    instance1 hashCode:- 366712642
    instance2 hashCode:- 1829164700
    
    Two different hashCode means there are 2 different objects of singleton class.
    Overcome Cloning issue:- To overcome this issue, override clone() method and throw an exception from clone method that is CloneNotSupportedException. Now whenever user will try to create clone of singleton object, it will throw exception and hence our class remains singleton.



    // JAVA code to explain overcome 
    // cloning issue with singleton
    class SuperClass implements Cloneable {
      int i = 10;
      
      @Override
      protected Object clone() throws CloneNotSupportedException  {
        return super.clone();
      }
    }
      
    // Singleton class
    class Singleton extends SuperClass {
      // public instance initialized when loading the class
      public static Singleton instance = new Singleton();
      
      private Singleton()    {
        // private constructor
      }
      
      @Override
      protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
      }
    }
      
    public class GFG {
      public static void main(String[] args) throws CloneNotSupportedException {
        Singleton instance1 = Singleton.instance;
        Singleton instance2 = (Singleton) instance1.clone();
        System.out.println("instance1 hashCode:- " 
                             + instance1.hashCode());
        System.out.println("instance2 hashCode:- " 
                             + instance2.hashCode()); 
      }
    }
    Output:-
    Exception in thread "main" java.lang.CloneNotSupportedException
     at GFG.Singleton.clone(GFG.java:29)
     at GFG.GFG.main(GFG.java:38)
    
    Now we have stopped user to create clone of singleton class. If you don;t want to throw exception you can also return the same instance from clone method.



    // JAVA code to explain overcome 
    // cloning issue with singleton
    class SuperClass implements Cloneable {
      int i = 10;
      
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return super.clone();
      }
    }
      
    // Singleton class
    class Singleton extends SuperClass {
      // public instance initialized when loading the class
      public static Singleton instance = new Singleton();
      
      private Singleton() {
        // private constructor
      }
      
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return instance;
      }
    }
      
    public class GFG {
      public static void main(String[] args) throws CloneNotSupportedException {
        Singleton instance1 = Singleton.instance;
        Singleton instance2 = (Singleton) instance1.clone();
        System.out.println("instance1 hashCode:- " 
                               + instance1.hashCode());
        System.out.println("instance2 hashCode:- "
                               + instance2.hashCode()); 
      }
    }
    Output:-
    instance1 hashCode:- 366712642
    instance2 hashCode:- 366712642
    
    Now, as hashcode of both the instances is same that means they represent a single instance.

Comments

Popular posts from this blog

gsutil Vs Storage Transfer Service Vs Transfer Appliance

SQL basic interview question