
In the real world of multicore processors, we have to write multi-threaded applications that would use all cores efficiently. This is very practical, when it comes to high-loaded servers (with a high amount of users trying to access service at the same time). For instance, after you have successfully introduced the new functionality on your web service or fixed some bugs, your first intuition would be to release the new version as soon as possible. At this point, when your users start to access your web service simultaneously, a situation may arise, when two or more users will initialize the same singleton or field value more than once. This is not a favorable type of scenario. It is also the main reason your applications should be ready to initialize its singletons (and not only) properly!
Double-Checked Locking Implementation Link to heading
public final class Singleton {
// Block access for two threads simultaneously
private volatile Singleton INSTANCE;
/**
* Blocks direct object creation
*/
private Singleton() {
}
public Singleton getInstance() {
// First check: two or more threads can pass this check
if (INSTANCE == null) {
synchronized (Singleton.class) {
// Second check: only one thread will initialize the variable instance
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
How It Works Link to heading
The code above does the singleton initialization in two major steps:
First Check: The first step would be to check, if the singleton
INSTANCE
has been already initialized. No big deal.Second Check with Synchronization: The second step is however very important. If the check for
INSTANCE
returns null and the singleton has not been initialized yet, in this case we first synchronize the thread access to get a unique access from only one thread. Afterwards we check theINSTANCE
of the singleton class again. These actions are performed in order to be sure that no other thread has already modified theINSTANCE
(between the first checkINSTANCE
for null value and synchronized block). Once a thread has received a unique access and checkedINSTANCE
for null, it initializes the singleton instance. Do not forget to declare the singleton instance asvolatile
.
Importance of Volatile Keyword Link to heading
Without the volatile
variable, another thread could read a half-initialized INSTANCE
variable in the moment when the
first thread is about to leave the synchronized block. The volatile
variable guarantees happens-before behaviour. This
means, that any write action related to the variable will take place prior to any reads. As a result, in the memory the
object will be created only once, while your code will be possible to use safely in a multithreaded environment.
Modern Approach: Enum Singleton Link to heading
However, starting Java 1.5 release, Enum
class was introduced. It does allow for an easy multithread-safe creation of
a singleton instance.
public enum Singleton {
INSTANCE;
}
Many programmers prefer Enums to handcrafting singletons by themselves. Personally I also share this approach and thus I don’t recommend using anything but Enum singletons in your multithreaded applications. This will reduce side-effects, errors and obvious memory leaks in your programs.
Practical Applications Link to heading
The Double Checked pattern itself does allow you to initialize any preferred variable in a multithreaded environment, not just a singleton class. Many interviewers tend to ask this question in the job interviews (especially if you are a junior in the beginning of your Software Development career) in order check your ability to understand the basics of multithreaded programming.