Reputation: 4864
Refering to this https://en.wikipedia.org/wiki/Double-checked_locking, we have:
// "Double-Checked Locking" idiom
class Foo {
private Helper helper;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
What is the purpose of the second test? Is it really possible that 2 threads could access the same critical section at the same time?
Upvotes: 1
Views: 404
Reputation: 6940
In addition to all other answers, a very good example is the Thread Safe Singleton pattern. You have again the same:
public static Singleton getInstanceDC() {
if (_instance == null) { // Single Checked
synchronized (Singleton.class) {
if (_instance == null) { // Double checked
_instance = new Singleton();
}
}
}
return _instance;
}
So basically performing the lock is a lot more expensive if compared to a pointer check instance != null
. The implementation also has to ensure that when the Singleton is initialized there will be no issues resulting from thread race conditions. So the main reason is the performance. If instance != null
(which will always be the case except the very first time), there is no need to do the expensive lock: Two threads accessing the initialized singleton simultaneously would be synchronized unnecessarily. This picture demonstrates it clear:
There is a lot more in Singletons then just the double checking:
Early and lazy instantiation in singleton pattern
Singleton and Serialization
Upvotes: 1
Reputation: 64904
To make it more obvious what could happen, consider this code:
if (helper == null) {
Thread.sleep(1000);
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
This is just an example so I don't care about InterruptedException
.
It should be clear that between passing the first test and entering the critical region, there is time for some other thread to come along and enter the critical region first.
Upvotes: 2
Reputation: 18533
Two threads cannot access the same critical section at the same time; by definition a critical section is mutually exclusive.
To answer your question, the first null
test is a cheap test without synchronization, and the second null
test checks the real state with synchronization.
The second test is necessary. Suppose we didn't have it, and the code looked like this:
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
helper = new Helper();
}
}
return helper;
}
Suppose thread A executes if (helper == null)
and it tests true
, so it goes into the if
block but execution gets suspended. No variables are updated yet.
Then thread B executes if (helper == null)
and it tests true
, so it goes into the if
block. Then it continues execution into the synchronized
block and initializes the helper
object and returns.
Now thread A continues execution, goes into the synchronized
block, overwrites helper
with a new object, and returns the object.
The problem we have is that helper
was initialized twice, with different objects.
That's why the second test is necessary.
Upvotes: 7