Java Singleton Design Pattern tells us to have a single copy of Java Object in memory throughout its execution life time.
In Java, it can be achieved by various ways but in Multi-threaded system, one of the beautiful ways to achieve this by applying double check locking. This will ensure faster and efficient way in multi-threaded environment.
Locking via synchronizing method:
Yes Singleton Design pattern in Java can also be achieved by synchronizing method but its inefficient.
Here is an example:
public class IAmSingle {
private static IAmSingle ref = null;
public static synchronized IAmSingle getInstance() {
if(ref == null) {
ref = new IAmSingle();
}
return ref;
}
}
Consider there are many threads trying to call getInstance method. Only one can acquire lock and rest will have to wait till the first one releases it (even for fetching this object). This makes it too inefficient in Multi-threaded environment.
Double Check Locking:
Consider below code snippet example for double Null check and locking for Singleton Design Pattern.
public class IAmSingle {
private static IAmSingle ref = null;
public static IAmSingle getInstance() {
if(ref == null) {
synchronized(IAmSingle.class) {
if(ref == null) {
ref = new IAmSingle();
}
}
}
return ref;
}
}
The above code is most optimized for Multi-threaded system. The first null check is essential after IAmSingle object has already been created by first thread entering the synchronized block and hence getting the instance of this object does not have to deal with acquiring lock by each thread.
Why second null check :
Ever wondered why you need to check object reference null again after acquire lock (as it has already been checked before acquiring lock). Consider the case where two or more threads passes first null check barrier and only one thread enters the lock, while making other waiting for that lock. When the first thread releases the lock after giving call to object creation, the rest others thread will not be aware of object creation and will go and acquire lock and create object (as it has already executed first null check when there is no object created). This will result in creating more than one IAmSingle object breaking the purpose of singleton design pattern.
Everything fine with above code?
So, does the above code is full proof of creating Single Object in Multi-threaded environment. Well actually its not. Any guess?
Well the answer lies hidden in way Java Virtual Machine creates object in heap memory area. To achieve optimization, JVM give instruction to create object and the code flows continues. It might happen (very rarely) while object is in process of being created, the next thread acquires lock and have passed call to second null check!!!!!! resulting in two objects being created!!!
Fix / Solution:
For Java 5 and above, making the reference variable volatile will fix the above problem as JVM will ensure that the object created and reference will happen very first. This way, the next thread will never be able to cross 2nd Null check barrier.
In Java, it can be achieved by various ways but in Multi-threaded system, one of the beautiful ways to achieve this by applying double check locking. This will ensure faster and efficient way in multi-threaded environment.
Locking via synchronizing method:
Yes Singleton Design pattern in Java can also be achieved by synchronizing method but its inefficient.
Here is an example:
public class IAmSingle {
private static IAmSingle ref = null;
public static synchronized IAmSingle getInstance() {
if(ref == null) {
ref = new IAmSingle();
}
return ref;
}
}
Consider there are many threads trying to call getInstance method. Only one can acquire lock and rest will have to wait till the first one releases it (even for fetching this object). This makes it too inefficient in Multi-threaded environment.
Double Check Locking:
Consider below code snippet example for double Null check and locking for Singleton Design Pattern.
public class IAmSingle {
private static IAmSingle ref = null;
public static IAmSingle getInstance() {
if(ref == null) {
synchronized(IAmSingle.class) {
if(ref == null) {
ref = new IAmSingle();
}
}
}
return ref;
}
}
The above code is most optimized for Multi-threaded system. The first null check is essential after IAmSingle object has already been created by first thread entering the synchronized block and hence getting the instance of this object does not have to deal with acquiring lock by each thread.
Why second null check :
Ever wondered why you need to check object reference null again after acquire lock (as it has already been checked before acquiring lock). Consider the case where two or more threads passes first null check barrier and only one thread enters the lock, while making other waiting for that lock. When the first thread releases the lock after giving call to object creation, the rest others thread will not be aware of object creation and will go and acquire lock and create object (as it has already executed first null check when there is no object created). This will result in creating more than one IAmSingle object breaking the purpose of singleton design pattern.
Everything fine with above code?
So, does the above code is full proof of creating Single Object in Multi-threaded environment. Well actually its not. Any guess?
Well the answer lies hidden in way Java Virtual Machine creates object in heap memory area. To achieve optimization, JVM give instruction to create object and the code flows continues. It might happen (very rarely) while object is in process of being created, the next thread acquires lock and have passed call to second null check!!!!!! resulting in two objects being created!!!
Fix / Solution:
For Java 5 and above, making the reference variable volatile will fix the above problem as JVM will ensure that the object created and reference will happen very first. This way, the next thread will never be able to cross 2nd Null check barrier.