Thursday, 26 June 2014

Singleton Design Pattern

singleton pattern restricts the instantiation of a class to one object. That is you can't create more than one object to this class. This is useful when exactly one object is needed to coordinate actions across the system.

Will see various approaches to construct singleton class and see the problems in them. Finally finds better solution.

Approach 1
Make the default constructor as private and create static instance inside the class itself. Provide a static getter method to get the instance of this class.

class SingleTon{
 static SingleTon instance = new SingleTon();
 
 private SingleTon(){
 
 }
 
 public static SingleTon getInstance(){
  return instance;
 }
}

Constructor for the SingleTon class has private access, so it can't be accessed from outside. 'getInstance' method simply the instance which is already created.

class SingleTonTest{
 public static void main(String args[]){
  SingleTon obj1 = SingleTon.getInstance();
  SingleTon obj2 = SingleTon.getInstance();
  System.out.print("Is obj1 and obj2 points to same object ");
  System.out.println((obj1==obj2));
 }
}

Output
Is obj1 and obj2 points to same object true

Problem in Approach 1
Object is created, even if needs or not. If this Object is resource intensive and if your Application never used it, then it is wastage of your resource.

Approach 2
Create the instance when needed. While creating an instance check whether instance already exist or not. If instance is not there, then create one and return it, else return the existing one.

class SingleTon{
 static SingleTon instance;
 
 private SingleTon(){
 
 }
 
 public static SingleTon getInstance(){
  if(instance == null){
   instance = new SingleTon();
  }
  return instance;
 }
}

class SingleTonTest{
 public static void main(String args[]){
  SingleTon obj1 = SingleTon.getInstance();
  SingleTon obj2 = SingleTon.getInstance();
  System.out.print("Is obj1 and obj2 points to same object ");
  System.out.println((obj1==obj2));
 }
}

Output
Is obj1 and obj2 points to same object true

Problem in Approach 2
In a Multi threaded Environment, Above code won't work.

Lets take a scenario, there are two threads t1 and t2 calls the getInstance method. Lets us assume, t1 got a chance first to enter into getInstance method, After checking for the condition(instance == null) which is true, it swapped out, and t2 come in, now for t2 also the condition (instance == null) is true, so there are two instances got created.

For Example, let us update the program to support threads.

class SingleTon{
 static SingleTon instance;
 
 private SingleTon(){
 
 }
 
 public static SingleTon getInstance(){
  if(instance == null){
   instance = new SingleTon();
  }
  return instance;
 }
}

class SingleTonTest extends Thread{
 SingleTon obj;
 
 public void run(){
  obj = SingleTon.getInstance();
 } 
 
 public static void main(String args[])throws Exception{
  SingleTonTest t1 = new SingleTonTest();
  SingleTonTest t2 = new SingleTonTest();
  
  t1.start();
  t2.start();
  
  t1.join();
  t2.join();
  
  System.out.print("Is t1.obj and t2.obj point to same object ");
  System.out.println((t1.obj==t2.obj));
 }
}

On Multiple runs the output is like below.

Run 1:
Is t1.obj and t2.obj point to same object false

Run 2:
Is t1.obj and t2.obj point to same object false

Run 3:
Is t1.obj and t2.obj point to same object false

Run 4:
Is t1.obj and t2.obj point to same object true

Run 5:
Is t1.obj and t2.obj point to same object true

Run 6:
Is t1.obj and t2.obj point to same object false

Approach 3
To solve the above problem, why can't we use synchronization. Looks like a good idea. Now change the code like below.

class SingleTon{
 static SingleTon instance = null;
 
 private SingleTon(){
 
 }
 
 public static SingleTon getInstance(){
  synchronized(SingleTon.class){
   if(instance == null){
    instance = new SingleTon();
   }
  }
  return instance;
 }
}

class SingleTonTest extends Thread{
 SingleTon obj;
 
 public void run(){
  obj = SingleTon.getInstance();
 } 
 
 public static void main(String args[])throws Exception{
  SingleTonTest t1 = new SingleTonTest();
  SingleTonTest t2 = new SingleTonTest();
  
  t1.start();
  t2.start();
  
  t1.join();
  t2.join();
  
  System.out.print("Is t1.obj and t2.obj point to same object ");
  System.out.println((t1.obj==t2.obj));
 }
}

Output
Is t1.obj and t2.obj point to same object true

Problem in Approach3
Don't you felt synchronization is a costliest process. If one thread acquire the lock on the class SingleTon, then remaining all thread must wait. So it is not effective solution.


Final Solution
Use double checked locking. First check whether the instance is null or not, then take the lock if the instance is null, else return the instance.

class SingleTon{
 static SingleTon instance = null;
 
 private SingleTon(){
 
 }
 
 public static SingleTon getInstance(){
  if(instance == null){
   synchronized(SingleTon.class){
    if(instance == null){
     instance = new SingleTon();
    }
   }
  }
  return instance;
 }
}






                                                             Home

No comments:

Post a Comment