Double Checked Locking in Singleton
Let's look at this code:
1 if (searchBox == null) {
2 synchronized (SearchBox.class) {
3 if (searchBox == null) {
4 searchBox = new SearchBox();
5 }
6 }
Let's try to reason about this. Let's say we have two threads A
and B
and let's assume that at least one of them reaches line 3 and observes searchBox == null
is true
. Two threads can not both be at line 3 at the same time because of the synchronized
block. This is the key to understanding why double-checked locking works. So, it must the case that either A
or B
made it through synchronized
first. Without loss of generality, say that that thread is A
. Then, upon seeing searchBox == null
is true, it will enter the body of the statement, and set searchBox
to a new instance of SearchBox
. It will then eventually exit the synchronized
block. Now it will be B
's turn to enter: remember, B
was blocked waiting for A
to exit. Now when it enters the block, it will observe searchBox
. But A
will have left just having set searchBox
to a non-null
value. Done.
By the way, in Java, the best way to implement a singleton is to use a single-element enum
type. From Effective Java:
While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.
This double check lock is only necessary if you are worried about many threads calling the singleton simultaneously, or the cost of obtaining a lock in general.
Its purpose is to prevent unnecessary synchronization, thereby keeping your code fast in a multi-threaded environment.
Check out this link for more information.
If you are running in Java 1.5 or greater, and you use the volatile
keyword in your double-check locked mechanism, it will work fine. As you are using the volatile
keyword, your example is not broken according to the same link above.
No, since you are obtaining lock on the SearchBox.class
, only one thread will enter the synchronized block at a time. So the first thread enters then finds searchBox
is null and creates it and then leaves the synchronized block, then the second thread enter the block then it finds that the searchBox
is not null because the first thread already created it so it will not create a new instance of searchBox
.
The double checked pattern is used to avoid obtaining the lock every time the code is executed. If the call are not happening together then the first condition will fail and the code execution will not execute the locking thus saving resources.