Do We Need Volatile When Implementing Singleton Using Double-Check Locking

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.

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 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.

double check locking without volatile (but with VarHandle release/acquire)

Yes, this is correct, and it is present on Wikipedia. (It doesn't matter that the field is volatile, since it is only ever accessed from VarHandle.)

If the first read sees a stale value, it enters the synchronized block. Since synchronized blocks involve happen-before relationships, the second read will always see the written value. Even on Wikipedia it says sequential consistency is lost, but it refers to the fields; synchronized blocks are sequentially consistent, even though they use release-acquire semantics.

So the second null check will never succeed, and the object is never instantiated twice.

It is guaranteed that the second read will see the written value, because it is executed with the same lock held as when the value was computed and stored in the variable.

On x86 all loads have acquire semantics, so the only overhead would be the null check. Release-acquire allows values to be seen eventually (that's why the relevant method was called lazySet before Java 9, and its Javadoc used that exact same word). This is prevented in this scenario by the synchronized block.

Instructions may not be reordered out and into synchronized blocks.

What is the point of making the singleton instance volatile while using double lock?

Without volatile the code doesn't work correctly with multiple threads.

From Wikipedia's Double-checked locking:

As of J2SE 5.0, this problem has been fixed. The volatile keyword now ensures that multiple threads handle the singleton instance correctly. This new idiom is described in The "Double-Checked Locking is Broken" Declaration:

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}

// other functions and members...
}

In general you should avoid double-check locking if possible, as it is difficult to get right and if you get it wrong it can be difficult to find the error. Try this simpler approach instead:

If the helper object is static (one per class loader), an alternative is the initialization on demand holder idiom

// Correct lazy initialization in Java 
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}

public static Helper getHelper() {
return HelperHolder.helper;
}
}

Is the following java code thread safe without volatile?

As anatolyg pointed out, you should make the field singleton private to avoid unwanted non-thread-safe accesses to that field.

Furthermore, even though in:

public static Singleton get(){
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
return singleton;
}

the return singleton; is outside of the synchronized block, this code is still thread-safe because the remaining code is inside the synchronized block, and consequently, all threads within that block will force a happens-before relation (i.e., there is no way threads will return null if the instance was properly set).

That being said beware that: quoting Holger

As long as the actual write to singleton happens-before the beginning
of the synchronized block, everything works. It only works
because there is at most one write
, definitely performed before the
return. If more writes were possible, they could happen concurrently
to the return statement that is outside the synchronized block.

There is a complete SO Thread that tackles why it is thread-safe to leave the return singleton outside the synchronized block.

Notwithstanding, I share the same opinion of others users such as this one

since the return doesn't take any CPU or anything, there is no reason
why it shouldn't be inside of the synchronized block. If it was then
the method can be marked as synchronized if we are in the Singleton
class here. This would be cleaner and better in case singleton gets
modified elsewhere.

That being said you don't need the volatile clause because you are synchronizing the initialization of the variable singleton. In this context, the synchronized clause guarantees not only that multiple threads will not access:

    if (singleton == null) {  
singleton = new Singleton();
}

but also that each thread sees the most up-to-date reference of the singleton field. Consequently, the race-condition of multiple threads assigning different object instances to the singleton field will not happen.

Some one say that no volatile for singleton variable is wrong.

Probably this person is mistaken your code with the double-checked locking pattern, which is a performance optimization over the version that you have shown. In your version threads will synchronize every time they call the method get, which is not necessary after the variable singleton has been correctly initialized. This is the overhead that the double-checked locking pattern tries to avoid. To achieve that the volatile is needed (you can read an in-depth explanation on this SO Thread), more information about this double-checked locking pattern can be found here.



Related Topics



Leave a reply



Submit