Core Java

Singleton Design Pattern In Java

Introduction:

Singleton Design Pattern is one of the basic and most useful Design Patterns in Java. It comes under the category of the creational pattern as defined in Gang of Four Design Patterns. The central idea behind this design pattern is to ensure that only a single instance of a class is created in the entire application scope.

It’s usage areas involve logging, caching, working with device driver objects etc. Frameworks like Spring make a heavy use of this pattern. java.lang.Runtime class is an example of a singleton class.

Characteristics of Singleton Class:

Any class is said to be implementing a Singleton design pattern if:

  1. Only one instance of that class is available throughout
  2. The instance of the class is globally accessible

Singleton Java Implementation:

From the defined characteristics, it’s easy to infer that we can define a Java class to be singleton by:

  1. A static member variable holding the instance of the class itself
  2. Defining a private constructor to prevent another instance to be created by the outside world
  3. An exposed instance getter method which is both public and static and returns an instance of itself.

So, the implementation would look almost like:

public class SingletonImpl {
    //instance member variable
    private static SingletonImpl obj;

    //private constructor
    private SingletonImpl(){
    
    }
    
    //getter for retrieving instance
    public static SingletonImpl getInstance() {
        if(obj == null) {
            obj = new SingletonImpl();
        }
        return obj;
    }
}

Note that our getInstance() method instantiates an object only for the first time. For all other times, a reference to the same object is returned.

Also in our implementation above, we have done a lazy initialization. It simply means that an object creation will be postponed until we receive the first request to access it.

Java Singleton In Multi-Threaded Environment:

Our previous implementation will work fine for a single-threaded application.

But how will it behave in a multi-threaded environment? Is it thread-safe?

The answer is ‘No’. It is because multiple threads might still try to invoke getInstance() method at the same moment of time for the very first time. It might, therefore, result in the creation of multiple instances of our class.

To solve this problem, we can choose to use one of the below strategies:
  1. Making getInstance() method synchronized:

    One obvious solution is to ensure a synchronized access to our getInstance() method:

    public class SingletonImpl {
        //instance member variable
        private static SingletonImpl obj;
    
        //private constructor
        private SingletonImpl(){
        
        }
        
        //getter for retrieving instance
        public static synchronized SingletonImpl getInstance() {
            if(obj == null) {
                obj = new SingletonImpl();
            }
            return obj;
        }
    }

    However, this might result in poor performance as for all requests for accessing our object a lock has to be acquired each time.

  2. Double-check Locking Strategy:

    A simple performance enhancement to our Singleton implementation would make use of a double-checking strategy:

    public class SingletonImpl {
        //instance member variable
        private static SingletonImpl obj;
    
        //private constructor
        private SingletonImpl(){
        
        }
        
        //getter for retrieving instance
        public static SingletonImpl getInstance() {
            if(obj == null) {
                synchronized(SingletonImpl.class) {    
                    if(obj == null) {
                        obj = new SingletonImpl();
                    }
                }
            }
            return obj;
        }
    }

    A small change of having a synchronized block results in high-performance gain as compared to our previous implementation. It is so because now an object lock will only be needed for the very first request of an instance. For all subsequent requests, the threads need not enter the synchronized context.

Conclusion:

In this tutorial, we discussed the Singleton Design Pattern and looked at its Java implementation in both single-threaded and multi-threaded environment.

Be the First to comment.

Leave a Comment

Your email address will not be published. Required fields are marked *