Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Double-Checked Locking is used at all?

I keep on running across code that uses double-checked locking, and I'm still confused as to why it's used at all.

I initially didn't know that double-checked locking is broken, and when I learned it, it magnified this question for me: why do people use it in the first place? Isn't compare-and-swap better?

if (field == null)
    Interlocked.CompareExchange(ref field, newValue, null);
return field;

(My question applies to both C# and Java, although the code above is for C#.)

Does double-checked locking have some sort of inherent advantage compared to atomic operations?

like image 600
user541686 Avatar asked May 23 '11 04:05

user541686


People also ask

Why do you need a double check lock?

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.

Is double-checked locking good?

Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment. Unfortunately, it will not work reliably in a platform independent way when implemented in Java, without additional synchronization.

What is double check locking in singleton?

Double checked locking of Singleton is a way to make sure that only one instance of Singleton class is created through an application life cycle.

Can the double-checked locking fail on single processor system?

Ans. There is no mapping of single ton with number of processor of the system. So double check locking will not fail depending on number of processor.


1 Answers

Does double-checked locking have some sort of inherent advantage compared to atomic operations?

(This answer only covers C#; I have no idea what Java's memory model is like.)

The principle difference is the potential race. If you have:

if (f == null)
    CompareExchange(ref f, FetchNewValue(), null)

then FetchNewValue() can be called arbitrarily many times on different threads. One of those threads wins the race. If FetchNewValue() is extremely expensive and you want to ensure that it is called only once, then:

if (f == null)
    lock(whatever)
        if (f == null)
            f = FetchNewValue();

Guarantees that FetchNewValue is only called once.

If I personally want to do a low-lock lazy initialization then I do what you suggest: I use an interlocked operation and live with the rare race condition where two threads both run the initializer and only one wins. If that's not acceptable then I use locks.

like image 54
Eric Lippert Avatar answered Oct 03 '22 05:10

Eric Lippert