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; } }
Problem :
- 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
- Step 3.a: If INSTANCE variable is null, then only instantiate
- 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
Problems of Lazy-Initialization :
- Although, we have done performance optimization for singleton design pattern with lazy initialization but still there are certain problems
- So, before we start coding singleton class in a multi-threaded environment we should understand problem with lazy initialization first
- 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
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 create another one
To overcome this situation, we need to execute lazy instance creation inside synchronized block
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
- Step 3.a: If INSTANCE variable is null, then only instantiate
- Step 3.b: Otherwise, return already instantiated INSTANCE variable
- 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
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
To conclude, we need to design singleton pattern in a such a way that
- once if singleton instance is created & available for use
- then no threads need 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; } }
Conclusion:
We have covered almost for every possible situation that may arise problem 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
Read Also:
- Java Serialization and De-Serialization Tutorial Index
- Serialization and De-Serialization in Java
- Serializable interface
- Transient keyword with Serialization in Java
- Transient keyword with static variable in Serialization
- Transient keyword with final variable in Serialization
- Serializing a variable with transient modifier or keyword
- Order of Serialization and De-Serialization
- Serialization with Aggregation
- Serialization with Inheritance
- Externalizable interface with example
- Serializable v/s Externalizable
- Importance of SerialVersionUID in Serialization
- Singleton Design pattern with Serialization
- How to stop Serialization in Java
- How to serialize and de-serialize ArrayList in Java
References:
- http://www.benchresources.net/singleton-design-pattern-with-java-serialization/
- https://blogs.oracle.com/JavaFundamentals/entry/using_enhanced_for_loops_with
- http://www.oracle.com/technetwork/articles/java/singleton-1577166.html
- http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking–clever–but-broken.html
Happy Coding !!
Happy Learning !!