Saturday, November 20, 2010

Can singleton be broken?

Singleton pattern
The most common code for creating a singleton pattern is as follows:

Code 1:
public class SingletonSample {
   private static SingletonSample instance;
   private SingletonSample(){}
   public static SingletonSample getInstance() {
         if (instance == null)                     // 1
         {instance = new SingletonSample();}       // 2
         return instance;                          // 3
   }
}
In the above design, the constructor is declared as private and the getInstance() method ensures that only a single object is created. This program will work well for single threaded program. But however, for multithreaded programs this pattern will fail inconsistently. Let’s have a look at how and where this pattern will fail.

a) Thread 1 enters the getInstance() method and determines that instance variable is null at line // 1.
b) Thread 1 enters if block but is pre-empted by thread 2 before executing line // 2.
c) Thread 2 calls getInstance() method and determines instance variable is null at line //1.
d) Thread 2 enters the IF block and creates a new object which is assigned to instance.
e) Thread 2 then returns the Object reference at line // 3.
f) Thread 2 is pre-empted by thread 1.
g) Thread 1 starts off where it left and creates another object at line // 2 and returns the reference at // 3.

Thus two Objects of the class are created when it was required to generate only one. A “synchronized” keyword is used to make multithreaded application thread safe and hence so, if we want to use the singleton pattern in multithreaded application we must synchronized the getInstance() method as follows :

Code 2:
synchronized public static SingletonSample getInstance() {
         if (instance == null)                     // 1
         {instance = new SingletonSample();}       // 2
         return instance;                          // 3
   }
The above code works fine for multithreaded access to getInstance() method but on deeper analysis we find that the ‘synchronization’ is actually required only for the first invocation as only on the first invocation will the line // 2 be actually executed, which also happens to be the line which needs to be synchronized. All other invocation apart from the first will get a not-null value and return the reference. However, it also adds up to performance issue as the complete getInstance() method is synchronized and that any other invocation will have to wait for the lock to be released to execute the getInstance().

Hence, in order improve the performance we can wrap up the line // 2 in a synchronized block instead of the complete method as follows

Code 3:
public static SingletonSample getInstance() {
         if (instance == null)                                                        // 1
         synchronized(SingletonSample.class){instance = new SingletonSample();}       // 2
         return instance;                                                             // 3
   }
However, in the above code too, we face the same problem as we had in code 1.Two threads can enter the IF block after finding instance is null. Now, Thread 1 enters the ‘synchronized’ block and creates the Object. Thread 2 also enters the synchronized block after Thread 1 releases the lock and also creates one more object. Now, in this case the Thread 2 does not check for the instance==null and directly creates a second Object.
Double-checked locking
A way to fix the problem in Code 3 is to put one more check, a second check on instance variable as follows:

Code 4:
public static SingletonSample getInstance() {
         if (instance == null)                                                        
         synchronized(SingletonSample.class){                      // 1               
             if(instance==null)                                    // 2
                instance = new SingletonSample();                  // 3
         }       
         return instance;                                          
   }
Thus by implementing the second check on instance variable makes it impossible for creation of more than one SingletonSample objects.
Since, this design uses two checks it’s called as “Double-checked locking”. Consider the following scenario.

a) Thread 1 enters the getInstance() method and the synchronized block at line as instance is null.
b) Thread 1 is pre-empted by thread 2.
c) Thread 2 enters the getInstance() and tries to acquire lock at // 1 as instance is still null but fails because Thread 1 already has a lock on the Object.
d) Thread 2 is pre-empted by Thread 1.
e) Thread 1 then executes line //2 and line // 3 to create an Object as instance is still null at line // 2.
f) Thread 1 is pre-empted by thread 2 and acquires a lock at line // 1.
g) Thread 2 does a null check at line // 2 and after finding that it is not null exits the block without creating a new Object.

Now, finally we can conclude that we are able to generate a single instance at last. But can we really do that? Let us have a look at the code 4 line 3 again. The problem with this line is that instance can become non-null even before the constructor executes completely. Let’s look at the following flow.

a) Thread 1 enters the getInstance() and the synchronized block at line // 1 as instance is null.
b) Thread 1 executes the code at line // 3. Now after the fully creating the Object but BEFORE the initializing the object.
c) Thread 1 is pre-empted by thread 2.
d) Thread 2 checks to see if instance is null. Because it is not, thread 2 returns a reference to a fully constructed but partially initialized Object.
e) Thread 2 is pre-empted by thread 1.
f) Thread 1 completes the constructor and initializes the object and returning the reference.

Solution
This problem is because of the Java memory problem and hence the solution to the above problem is to use synchronized getInstance() as in Code // 2 or static as follows :

public static Singleton getInstance()
  {
    return instance;
  } 

No comments:

Post a Comment