Reputation: 1377
I have seen some similar questions,but I still have some confusions.
code is here:
private volatile static DoubleCheckSingleton instance;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance(){
if(instance==null){ //first
synchronized (DoubleCheckSingleton.class){
if(instance==null){ // second
instance=new DoubleCheckSingleton();
}
}
}
return instance;
}
In this question Why is volatile used in double checked locking, it says that without a volatile
keyword, a thread may assign the instance variable before the constructor finishes, so another thread may see a half-constructed object, which can cause serious problem.
But I don't understand how volatile can solve the problem. Volatile
is used to ensure visibility, so when thread A assign a half-constructed object to instance variable, the other thread can immediately see the change, which makes the situation worse.
How does volatile
solve the problem, somebody please explain to me. Thanks!
Upvotes: 1
Views: 940
Reputation: 969
Double checked locking spent a good bit of time as an anti-pattern, according to Java Concurrency In Practice et. al. At the time they suggested using something like the Lazy Holder pattern instead.
https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
Brian Goetz wrote a good article on how DCL was broken here https://www.javaworld.com/article/2074979/double-checked-locking--clever--but-broken.html
As StevenC pointed out to me, since the update to the Java Memory Model it will work properly, although in spite of this I think the Lazy Holder is still a nice alternative. It is clean to implement, and avoids the need of the volatile. Improper implementation of DCL will cause hard to find bugs.
Upvotes: 2
Reputation: 8945
To put it another way, suppose we have two perfectly-racing threads:
... and, quite likely, neither thread is presently aware that something is wrong. It's likely that both of them think that they have a DoubleCheckSingleton
and are using them just as they should be ... neither of them realizing that there are two. (So, sit back and watch data-structures getting trashed left and right as your app nose-dives into the ground.)
Even worse, this is exactly the kind of bug that "happens once-in-a-blue-moon." Which is to say that it never happens on any developer machine, but it happens five minutes after you deploy to production ... ;-)
The volatile
keyword basically says that "this storage location, itself is a shared resource." Which it is.
Now also: the best practice, I think, is to initialize all of your synchronization objects before you start to launch threads or subprocesses. All of these entities can now refer to these now already-existing objects and can use them, but none of these players ever create them.
Upvotes: -2
Reputation: 27115
a thread may assign the instance variable before the constuctor finishes
That's not actually true. The assignment is right there in the code example:
instance=new DoubleCheckSingleton()
Obviously, the thread that performs that assignment can not possibly do the assignment before the constructor call has returned.
The problem is, when two different threads are running on two different processors without any synchronization, they will not necessarily agree upon the order in which assignments happen. So, even though thread A assigned the fields of the new object (inside the new DoubleCheckSingleton()
call) before it assigned instance
, thread B potentially could see those assignments out-of-order. Thread B could see the assignment to instance
before it sees some of the other things that new DobuleCheckSingleton()
did.
Declaring instance
to be volatile
synchronizes the threads. volatile
guarantees that everything thread A did before it assigned a volatile
variable will become visible to thread B when thread B fetches the value of the volatile
variable.
Upvotes: 4