What is Singleton?
Singleton is a software design pattern which ensures only one object of a type is created. This is useful when we want to create an object, which manages or coordinates some tasks in the system. A WindowManager, a FileSystem, MousePointer would be good examples for places where Singleton design pattern can be applied.
The call to Singleton.getInstance()creates a new Singleton type object, provided that the variable “instance” is null and returns it. If “instance” is not null getInstance() returns the “instance”. The variable “instnace” which is null at the startup, is assigned an object at the first call to Singleton.getInstance() and subsequent call to Singleton.getInstance() returns the object created at the first call.
In this way, it ensures only one object of Singleton is there, regardless of the number of times the getInstance() is being called. This works perfectly in a single threaded environment, if only one thread calls getInstance(). But if there are multiple threads say A and B, thread A may reach to the null check where thread B initiate execution. however even at this point, instance is still null to the thread A. Now thread B executes null check the instance and creates a new Singleton. Then thread A again executes and creates another singleton since instance is still null for A. Thus, when there are multiple thread accessing the Singleton class, above code does not guarantee the Singletoness.
Multi threaded approach
To avoid the problem mentioned above, we have to ensure only one thread accesses the getInstance() at a particular moment. The solution to ensure the mutual exclusion for threads in Java is synchronizing the getInstance().
The above methods will provide singletonness. However there could be a performance issue as each and every call needs to execute a synchronized block, which will result in delaying the execution process. In an environment where get instance() is called frequently, this may generate a considerable performance degrade.
Double check approach
With the aim of mitigating the performance issues that arise due to the Multi-Thread approach, Double check approach was introduced. It goes inside to the synchronized block only after performing instance null check. If the null check fails, it will not go inside to the synchronized block.
Double check may also can be a ground to some other problems due to many reasons. One among such problems is compiler optimization. Instance = new type() seems to be a single operation, but there are multiple operations hidden inside. They are the locating memory for the instance, creating the new object by calling the constructor, assigning the newly created object to the “instance” reference. The compiler optimization may reorder these operations depending on the compiler implementation.
Step 1) Assign object to the “instance” reference
Step 2) Constructor code
If the compiler is being ordered in the above way and if another thread start executing get instance(), the instance is not null, since step 1 is over, but only the default values of the attributes of the object can be seen, without the values specified in the constructor. Thus the other objects get the wrong values.
Make instance volatile
By making the instance variable volatile above issue can be sorted out, since the partial values of volatile variables are not visible to other threads.
A better solution with java class loading(lazy initialization)
JVM guarantees a class is loaded only once. We add an inner class to store the instance. Since instance is a static, it is being initialized at the time of class is being loaded. Thus only one instance is created as the class is loaded only once. One essential thing to be noted is InstanceHolder inner class is loaded only at the first call to getInstance(), delaying the instance creation until the object is needed, which is known as lazy initialization.