Let's go through the below a bit about singleton and then the double checking problem
What is singleton?
>>A singleton is simply a class that is instantiated exactly once
Objective of singleton?
>>There should be only one instance allowed for a class
>>There should allow global point of access to that single instance
Why singletons are required?
>>Singletons typically represent a system component that is intrinsically unique, such as Java Runtime, the window manager or file system
>>Singletons are used to store data that it is used/updated across multiple components. The data in one component is usually important to another component, so everything is managed in one central object.
Ways to implement
There are 2 ways to implement singletons. In Both ways, We suppress the constructor and don’t allow even a single instance for the class. But we declare a static member to hold the sole instance of our singleton class
Early Initialization
At the time of loading the class, instance gets created. JVM guarantees that the instance will be created before any thread access the static member. getInstance() method simply returns the instance that was already created.
If your application always creates and uses singleton instance or the overhead of creation and runtime aspects of the singleton are not onerous, this early initialization is preferred.
==============================================
public class SingletonEarly {
private final static SingletonEarly instance = new SingletonEarly();
private SingletonEarly() {
}
public SingletonEarly getInstance() {
return instance;
}
}
==============================================
Lazy Initialization
==============================================
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
// Suppressing creating a new instances
}
public SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
==============================================
Double-Checked Locking Problem
In earlier times (prior to JDK 1.6) a simple uncontended synchronization block was expensive and that lead many people to write double-checked locking to write lazy initialization code. The double-checked locking idiom tries to improve performance by avoiding synchronization over the common code path after the helper is allocated. But the Double Checked Locking never worked because of the limitations of pervious JMM.
This is now fixed by new JMM (JDK 1.5 onwards) using volatile keyword.
==============================================
public class SingletonLazy {
private static SingletonLazy _instance;
private SingletonLazy() {
// Suppressing creating a new instances
}
public SingletonLazy getInstance() {
if (_instance == null) {
synchronized (SingletonLazy.class) {
if (_instance == null) {
_instance = new SingletonLazy();
}
}
}
return _instance;
}
}
==============================================
Why above code idiom is broken in current JMM ?
Double Checked Locking relies on the un synchronized use of _instance field. This appears harmless, but it is not. Suppose Thread A is inside sycnhronized block and it is creating new Singleton instance and assigning to _instance variable, while thread B is just entering the getInstance() method. Consider the effect on memory of this initialization. Memory for the new Singleton object will be allocated; the constructor for Singleton will be called, initializing the member fields of the new object; and the field resource of SomeClass will be assigned a reference to the newly created object. There could be two scenarios now
>> Suppose Thread A has completed initialization of _instance and exits synchronized block as thread B enters getInstance(). By this time, the _instance is fully initalized and Thread A has flushed its local memory to main memory (write barriers). Singleton's member fields may refer other objects stored in memory which will also be flushed out.. While Thread B may see a valid reference to the newly created _instance, but because it didn't perform a read barrier, it could still see stale values of _instance's member fields.
>> Since thread B is not executing inside a synchronized block, it may see these memory operations in a different order than the one thread A executes. It could be the case that B sees these events in the following order (and the compiler is also free to reorder the instructions like this): allocate memory, assign reference to resource, call constructor. Suppose thread B comes along after the memory has been allocated and the resource field is set, but before the constructor is called. It sees that resource is not null, skips the synchronized block, and returns a reference to a partially constructed Resource! Needless to say, the result is neither expected nor desired.
Fixed double-checked Locking using volatile in new JMM (multi-threaded singleton pattern JDK 1.5)
The following code makes the helper volatile so as to stop the instruction reordering. This code will work with JDK 1.5 onwards only.
=================================================
public class SingletonLazy {
private volatile static SingletonLazy _instance;
private SingletonLazy() {
// Suppressing creating a new instances
}
public SingletonLazy getInstance() {
if (_instance == null) {
synchronized (SingletonLazy.class) {
if (_instance == null) {
_instance = new SingletonLazy();
}
}
}
return _instance;
}
}
=================================================
Hope this give an insight to The Double Checked Locking Problem, Happy Learning :)
No comments:
Post a Comment