Why Is Volatile Used in Double Checked Locking

Why is volatile used in double checked locking

A good resource for understanding why volatile is needed comes from the JCIP book. Wikipedia has a decent explanation of that material as well.

The real problem is that Thread A may assign a memory space for instance before it is finished constructing instance. Thread B will see that assignment and try to use it. This results in Thread B failing because it is using a partially constructed version of instance.

why java double check lock singleton must use the volatile keyword?

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.

Double-checked locking without volatile

First things first: what you are trying to do is dangerous at best. I am getting a bit nervous when people try to cheat with finals. Java language provides you with volatile as the go-to tool to deal with inter-thread consistency. Use it.

Anyhow, the relevant approach is described in
"Safe Publication and Initialization in Java" as:

public class FinalWrapperFactory {
private FinalWrapper wrapper;

public Singleton get() {
FinalWrapper w = wrapper;
if (w == null) { // check 1
synchronized(this) {
w = wrapper;
if (w == null) { // check2
w = new FinalWrapper(new Singleton());
wrapper = w;
}
}
}
return w.instance;
}

private static class FinalWrapper {
public final Singleton instance;
public FinalWrapper(Singleton instance) {
this.instance = instance;
}
}
}

It layman's terms, it works like this. synchronized yields the proper synchronization when we observe wrapper as null -- in other words, the code would be obviously correct if we drop the first check altogether and extend synchronized to the entire method body. final in FinalWrapper guarantees iff we saw the non-null wrapper, it is fully constructed, and all Singleton fields are visible -- this recovers from the racy read of wrapper.

Note that it carries over the FinalWrapper in the field, not the value itself. If instance were to be published without the FinalWrapper, all bets would be off (in layman terms, that's premature publication). This is why your Publisher.publish is disfunctional: just putting the value through final field, reading it back, and publishing it unsafely is not safe -- it's very similar to just putting the naked instance write out.

Also, you have to be careful to make a "fallback" read under the lock, when you discover the null wrapper, and use its value. Doing the second (third) read of wrapper in return statement would also ruin the correctness, setting you up for a legitimate race.

EDIT: That entire thing, by the way, says that if the object you are publishing is covered with final-s internally, you may cut the middleman of FinalWrapper, and publish the instance itself.

EDIT 2: See also, LCK10-J. Use a correct form of the double-checked locking idiom, and some discussion in comments there.

The need for volatile modifier in double checked locking in .NET

Volatile is unnecessary. Well, sort of**

volatile is used to create a memory barrier* between reads and writes on the variable.

lock, when used, causes memory barriers to be created around the block inside the lock, in addition to limiting access to the block to one thread.

Memory barriers make it so each thread reads the most current value of the variable (not a local value cached in some register) and that the compiler doesn't reorder statements. Using volatile is unnecessary** because you've already got a lock.

Joseph Albahari explains this stuff way better than I ever could.

And be sure to check out Jon Skeet's guide to implementing the singleton in C#



update:

*volatile causes reads of the variable to be VolatileReads and writes to be VolatileWrites, which on x86 and x64 on CLR, are implemented with a MemoryBarrier. They may be finer grained on other systems.

**my answer is only correct if you are using the CLR on x86 and x64 processors. It might be true in other memory models, like on Mono (and other implementations), Itanium64 and future hardware. This is what Jon is referring to in his article in the "gotchas" for double checked locking.

Doing one of {marking the variable as volatile, reading it with Thread.VolatileRead, or inserting a call to Thread.MemoryBarrier} might be necessary for the code to work properly in a weak memory model situation.

From what I understand, on the CLR (even on IA64), writes are never reordered (writes always have release semantics). However, on IA64, reads may be reordered to come before writes, unless they are marked volatile. Unfortuantely, I do not have access to IA64 hardware to play with, so anything I say about it would be speculation.

i've also found these articles helpful:

http://www.codeproject.com/KB/tips/MemoryBarrier.aspx

vance morrison's article (everything links to this, it talks about double checked locking)

chris brumme's article (everything links to this)

Joe Duffy: Broken Variants of Double Checked Locking

luis abreu's series on multithreading give a nice overview of the concepts too

http://msmvps.com/blogs/luisabreu/archive/2009/06/29/multithreading-load-and-store-reordering.aspx

http://msmvps.com/blogs/luisabreu/archive/2009/07/03/multithreading-introducing-memory-fences.aspx



Related Topics



Leave a reply



Submit