Singleton design pattern ensures that at any time there can only be one instance of a class and provide a global point of access to it.
Classes implementing Thread Pools, database Connection pools, caches, loggers etc are generally made Singleton so as to prevent any class from accidently creating multiple instances of such resource consuming classes.
Singleton design Pattern implementation:-
- Declare a default private constructor.
- Declare a private static variable to hold single instance of class.
- Declare a public static function that returns the single instance of class.
- Do “lazy initialization” in the accessor function.
class Singleton {
// 1. Make all constructors private
private Singleton() {}
// 2. Declare a private static variable to hold single instance of class
private static Singleton INSTANCE = null;
// 3. Declare a public static function that returns the single instance of class
public static Singleton getInstance() {
// 4. Do "lazy initialization" in the accessor function.
if(INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
public class SingletonTest
{
public static void main(String[] args)
{
System.out.println("Singleton Test!");
// always return the same instance
System.out.println("Singleton instance id :"+Singleton.getInstance());
System.out.println("Singleton instance id :"+Singleton.getInstance());
System.out.println("Singleton instance id :"+Singleton.getInstance());
// can't create instance of Singleton class
//Singleton instance2 = new Singleton(); // Compilation Error
}
}
public static Singleton getInstance() {
if(INSTANCE == null) { // 1
INSTANCE = new Singleton(); // 2
}
return INSTANCE; // 3
}
The above code can lead to creation of multiple instances in a multithreaded environment. Let’s take a closer look at the getInstance() method.
If one thread enters getInstance() method and finds the INSTANCE to be null at step 1 and enters the IF block and before it can execute step 2 another thread enters the method and executes step 1 which will be true as the INSTANCE is still null, then it might lead to a situation (Race condition) where both the threads executes step 2 and create two instances of the Singleton class.
If one thread enters getInstance() method and finds the INSTANCE to be null at step 1 and enters the IF block and before it can execute step 2 another thread enters the method and executes step 1 which will be true as the INSTANCE is still null, then it might lead to a situation (Race condition) where both the threads executes step 2 and create two instances of the Singleton class.
// Recreate threading issue during instantiation of Singleton class
class Singleton {
// 1. Make all constructors private
private Singleton() {}
// 2. Declare a private static variable to hold single instance of class
private static Singleton INSTANCE = null;
// 3. Declare a public static function that returns the single instance of class
public static Singleton getInstance() {
if(INSTANCE == null) {
try {
// If there is delay in creation of object then the threads might create multiple instances
Thread.sleep(10);
INSTANCE = new Singleton();
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
}
return INSTANCE;
}
}
public class SingletonThreadingTest implements Runnable
{
public void run() {
System.out.println("Singleton instance id :"+Singleton.getInstance());
}
public static void main(String[] args)
{
System.out.println("Singleton Test!");
SingletonThreadingTest sei1 = new SingletonThreadingTest();
Thread thread1 = null;
for(int i=0; i< 2;i++) {
thread1 = new Thread(sei1);
// might create mutliple instances of Singleton class
thread1.start();
}
}
}
Eager instantiation in Singleton design pattern
We can prevent this issue by making the getInstance() method ‘synchronized’ but that will decrease the performance of your system as only single thread can access the getInstance method at a time. The other way could be to eagerly instantiate the INSTANCE variable.
Also if your application always creates and uses the singleton instance and overhead of creation of singleton instance is not a major point of concern then also we can create the instance of the class eagerly.
// Eager instantiation of Singleton class
class Singleton {
// 1. Make all constructors private
private Singleton() {}
// 2. Eagerly declare a private static variable to hold single instance of class
private static Singleton INSTANCE = new Singleton();
// 3. Declare a public static function that returns the single instance of class
public static Singleton getInstance() {
return INSTANCE;
}
}
public class SingletonEagerInstantiationTest
{
public static void main(String[] args)
{
System.out.println("Singleton Test!");
// always return the same instance
System.out.println("Singleton instance id :"+Singleton.getInstance());
System.out.println("Singleton instance id :"+Singleton.getInstance());
System.out.println("Singleton instance id :"+Singleton.getInstance());
// can't create instance of Singleton class
//Singleton instance2 = new Singleton(); // Compilation Error
}
}
Double checked locking method
Another way to prevent multithreading issue is to use ‘Double checked Locking method’ to reduce the use of synchronization in getInstance() method. In this method the synchronized block is placed inside the first NULL check condition so the synchronized block will be executed only initially when the initialization of instance is not done. Its called double checked as we check the nullability of INSTANCE twice.
// Double checked locking method
class Singleton {
// 1. Make all constructors private
private Singleton() {}
// 2. volatile keyword ensures that multiple threads handle the INSTANCE variable
// correctly when it is being initiaized.
private volatile static Singleton INSTANCE = null;
// 3. method is not made synchronized
public static Singleton getInstance() {
// check that INSTANCE is initialized and if not then enter
// synchronized block
if(INSTANCE == null) {
synchronized(Singleton.class) {
// inside the block check again for initialization
if(INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
public class SingletonDoubleCheckedLockingTest implements Runnable
{
public void run() {
System.out.println("Singleton instance id :"+Singleton.getInstance());
}
public static void main(String[] args)
{
System.out.println("Singleton Test!");
SingletonDoubleCheckedLockingTest sei1 = new SingletonDoubleCheckedLockingTest();
Thread thread1 = null;
for(int i=0; i< 2;i++) {
thread1 = new Thread(sei1);
// might create mutliple instances of Singleton class
thread1.start();
}
}
}
You can read more about singleton design pattern from the below links:-
http://en.wikipedia.org/wiki/Singleton_pattern