Java – How to construct a singleton class in a multi-threaded environment ?

In this article, we will discuss how to create or construct a singleton class in a multi-threaded environment

This is one of the top interview questions for experienced Java developers. We will list down series of questions before delving into details,

  • Do you know Singleton design pattern?
  • Write code for Singleton design pattern?
  • But this is poorly written code, can you write some improved or performance oriented code ?
  • How will you handle singleton design pattern in a multi-threaded environment?
  • Explain Double-Checked Locking pattern?

Singleton design pattern:

Singleton design pattern is the

  • solution proposed to return same instance every time
  • restrict instantiation of a class more than once
  • exactly one copy is available at any given point of time
  • ensures only one instance is available in a Java Virtual Machine

Q) How to check, whether 2 instances are same or different ?

  • Answer is always check hash code of the returned instance
  • If it is Same, then both instances are same and it is singleton
  • If it is Different, then both are different instances and there is something wrong with program logic

1. Singleton design pattern with Eager Instantiation :

Basic steps to create Singleton class

  • Step 1: private static variable of the INSTANCE of the same class (this is only time instance of this class get created)
  • Step 2: Provide private constructor to restrict instatiation from outside class
  • Step 3: Provide public static getInstance() method returning same INSTANCE every time
  • Note: These are steps for eager initialization

SingletonDesignPatternWithEagerInitialization.java

package in.bench.resources.singleton.design.pattern;

public class SingletonDesignPatternWithEagerInitialization {

	// Step 1: private static variable of INSTANCE variable
	private static SingletonDesignPatternWithEagerInitialization
	INSTANCE = new SingletonDesignPatternWithEagerInitialization();

	// Step 2: private constructor
	private SingletonDesignPatternWithEagerInitialization() {

	}

	// Step 3: Provide public static getInstance() method
	// returning same INSTANCE same time
	public static SingletonDesignPatternWithEagerInitialization
		getInstance() {
		return INSTANCE;
	}
}

1.1 Issues with above approach :

  • The above written code is very poor in terms of performance
  • because program returns singleton instance eagerly
  • i.e.; it instantiates and keep instance ready to be available
  • even before asking to return

2. Lazy Instantiation :

We can actually write more improved version of above code with Lazy initialization

Basic steps to create Singleton class using Lazy Initialization

  • Step 1: Just declare private static variable of the same class (beware don’t instantiate)
  • Step 2: Provide private constructor to restrict instatiation from outside class
  • Step 3: Provide public static getInstance() method  and check
    1. Step 3.a: If INSTANCE variable is null, then only instantiate
    2. Step 3.b: Otherwise, return already instantiated INSTANCE variable
  • Note: These are steps for lazy initialization

Let us move on,

SingletonDesignPatternWithLazyInitialization.java

package in.bench.resources.singleton.design.pattern;

public class SingletonDesignPatternWithLazyInitialization {

    // Step 1: private static variable of INSTANCE variable
    private static SingletonDesignPatternWithLazyInitialization
    		INSTANCE;

    // Step 2: private constructor
    private SingletonDesignPatternWithLazyInitialization() {

    }

    // Step 3: Provide public static getInstance() method
    // returning INSTANCE after checking
    public static SingletonDesignPatternWithLazyInitialization
    		getInstance() {

        if(null == INSTANCE){
            INSTANCE = new
                 SingletonDesignPatternWithLazyInitialization();
        }
        return INSTANCE;
    }
}

3. Singleton design pattern in a multi-threaded environment

3.1.1 Issues with Lazy-Initialization approach :

  • Although, we have done performance optimization for singleton design pattern with lazy initialization but still there are certain issues with above approach
  • So, before we start coding singleton class in a multi-threaded environment, first we should understand problem with lazy initialization
  • In the above example for Lazy Initialization, suppose 2 or more threads execute in parallel or concurrent, then there may be a issue with multiple instances being instantiated as explained in the below step

3.1.2 Let us understand in steps:

  • Thread-1 got the chance and it is put into execution
  • It finds the INSTANCE to be null and therefore Thread-1 instantiates
  • Concurrently, if any other thread got chance and if it tries to executes, then there may be a possibility of new instance is getting created, although it is 50 % chance
  • Because, new Thread-1 haven’t completed with creation of singleton INSTANCE and another thread at the same time finds singleton INSTANCE to be null and tries to creates another one

To overcome this situation, we need to execute lazy instance creation inside synchronized block

3.2 Solution for lazy initialization :

Basic steps to create Singleton class using Lazy Initialization in a Multi-threaded environment

  • Step 1: Just declare private static variable of the same class (beware don’t instantiate)
  • Step 2: Provide private constructor to restrict instantiation from outside class
  • Step 3: Provide public static getInstance() method  and check
    1. Step 3.a: If INSTANCE variable is null, then only instantiate
    2. Step 3.b: Otherwise, return already instantiated INSTANCE variable
    3. Synchronized: Put both above checks inside synchronized block
  • Step 4: In addition to above detailed steps, also make INSTANCE variable as volatile. This will help getting latest updated copy every time, as it will read from main memory than in its own CPU-cache area
  • Note: If your singleton INSTANCE is going to be executed in a single threaded environment, then there is no need of making INSTANCE variable as volatile

SingletonDesignPatternInMultiThreadedEnvironment.java

package in.bench.resources.singleton.design.pattern;

public class SingletonDesignPatternInMultiThreadedEnvironment {

    // Step 1: private static variable of INSTANCE variable
    private static volatile
    	SingletonDesignPatternInMultiThreadedEnvironment INSTANCE;

    // Step 2: private constructor
    private SingletonDesignPatternInMultiThreadedEnvironment() {

    }

    // Step 3: Provide public static getInstance() method
    // returning INSTANCE after checking
    public static SingletonDesignPatternInMultiThreadedEnvironment
    	getInstance() {

        // synchronized block
        synchronized
          (SingletonDesignPatternInMultiThreadedEnvironment.class){
          if(null == INSTANCE){
              INSTANCE =
                new
                SingletonDesignPatternInMultiThreadedEnvironment();
          }
          return INSTANCE;
        }
    }
}

This way, we can assure that every time single & same instance is returned

4. Double-checked locking – DCL

4.1 Performance issue with above approach:

But again, there is a performance issue with above program. Let us understand,

  • Assume that ONE instance is created and available for use (i.e.; singleton instance)
  • And with this single & same instance, some thread (Thread-Arya) is executing in a multi-threaded environment
  • Now suppose a new thread (Thread-Surya) got execution cycle and trying to get singleton instance, although it is already created & available for use
  • But Thread-Surya has to wait till Thread-Arya releases lock or comes out of synchronized block
  • This is bad and poor situation, reason being singleton instance is already created and still it has to wait to get that instance
  • Ideally speaking, Thread-Surya doesn’t need to wait for Thread-Arya to release lock and then check condition and then proceed with its execution

4.2 Design singleton pattern in a such a way that

  • Once, if singleton instance is created & available for use
  • Then no threads needs to wait
  • Rather, it should to get singleton instance and continue with its execution

To design such pattern, we need to look into double-checked locking design pattern

Let us move on and see exactly Double-Checked Locking Pattern in details

SingletonDesignPatternWithDCL.java

package in.bench.resources.singleton.design.pattern;

public class SingletonDesignPatternWithDCL {

    // Step 1: private static variable of INSTANCE variable
    private static volatile SingletonDesignPatternWithDCL
    		INSTANCE;

    // Step 2: private constructor
    private SingletonDesignPatternWithDCL() {

    }

    // Step 3: Provide public static getInstance() method
    // returning INSTANCE after checking
    public static SingletonDesignPatternWithDCL getInstance() {

        // double-checking lock
        if(null == INSTANCE){

            // synchronized block
            synchronized (SingletonDesignPatternWithDCL.class) {
                if(null == INSTANCE){
                    INSTANCE = new SingletonDesignPatternWithDCL();
                }
            }
        }
        return INSTANCE;
    }
}

5. Conclusion:

We have covered almost for every possible situation that may arise performance issues while dealing with Singleton design pattern, like

  • Eager initialization
  • Lazy initialization
  • Lazy initializtion in a multi-threaded environment
  • Double checking lock desing pattern

It is a design choice to choose from above listed approaches to begin with, as starting with double-checking lock in a non-threaded environment yields poor results

So, it is always design choice to look that which approach fits our case perfectly for our business requirement

Related Articles:

References:

Happy Coding !!
Happy Learning !!

Java - How to Serialize and De-Serialize ArrayList ?
Java - How to stop Serialization ?