16

the page at http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html says that double-checked locking is flawed in java. I'm just wondering does it also apply to other languages (C#, Vb, C++, etc)

I've read Double checked locking pattern: Broken or not?, Is this broken double checked locking?, How to solve the "Double-Checked Locking is Broken" Declaration in Java? to be truthful i don't know what the common consensus is. some say yes its broken others say no.

Anyway, my question is does it also apply to other languages (C#, Vb, C++, etc)

Community
  • 1
  • 1
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • According to expert opinion that I have read it should be safe in C# as long as `volatile` is used. Similarly, I believe one of the newer version of java makes it safe. I"m not really confident of this nor do I have the necessary expertise in the other languages so I'm not willing to post an answer. – Brian Gideon May 11 '11 at 03:09
  • 1
    Double-checking is not broken considering you have java >= 1.5 and apply the correct pattern and that Joshua Bloch has enough authority for you :) – denis.solonenko May 11 '11 at 03:12
  • 4
    Yeah, in Java >= 1.5, with a `volatile`, it works. But that doesn't necessarily mean you should use it: http://www.ibm.com/developerworks/library/j-jtp03304/ – T.J. Crowder May 11 '11 at 03:20
  • In general, this depends from your locking and visibility semantics, i.e. from the memory model of your language's implementation (or your multithreading/locking libraries). – Paŭlo Ebermann May 11 '11 at 03:26
  • @T.J. Crowder - your should definitely avoid it until really-really needed, I agree to that. But it is not broken per se.. – denis.solonenko May 11 '11 at 03:27
  • This can help with C++ http://stackoverflow.com/questions/6915/thread-safe-lazy-contruction-of-a-singleton-in-c – denis.solonenko May 11 '11 at 03:29

5 Answers5

10

Double checked locking is safe in Java, PROVIDED THAT:

  1. the instance variable is declared as volatile, AND
  2. the JVM correctly implements the JSR-133 specification; i.e. it is compliant with Java 5 and later.

My source is the JSR-133 (Java Memory Model) FAQ - Jeremy Manson and Brian Goetz, February 2004. This is confirmed by Goetz in a number of other places.

However, as Goetz says, this is an idiom whose time has passed. Uncontended synchronization in Java is now fast, so he recommends that you just declare the getInstance() method as synchronized if you need to do lazy initialization. (And I imagine that this applies to other languages too ...)

Besides, all things being equal, it is a bad idea to write code that works in Java 5 but is unreliable in older JVMs.


OK, so what about the other languages? Well, it depends on how the idiom is implemented, and often on the platform.

  • C# - according to https://stackoverflow.com/a/1964832/139985, it is platform dependent whether the instance variable needs to be volatile. However, Wikipedia says that if you do use volatile or explicit memory barriers, the idiom can be implemented safely.

  • VB - according to Wikipedia the idiom can be implemented safely using explicit memory barriers.

  • C++ - according to Wikipedia the idiom can be implemented safely using volatile in Visual C++ 2005. But other sources say that in general the C++ language specification doesn't provide sufficient guarantees for volatile to be sure. However double-checked locking can be implemented in the context of the C++ 2011 language revision - https://stackoverflow.com/a/6099828/139985.

(Note: I'm just summarizing some sources I found which seem to me to be recent ... and sound. I'm not C++, C# or VB expert. Please read the linked pages and make your own judgements.)

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    The question asks *"Anyway, my question is does it also apply to other languages (C#, Vb, C++, etc)"* Not Java. We covered Java in the comments. – T.J. Crowder May 11 '11 at 03:36
  • 3
    @T.J. - I know, but the Question also spreads misinformation, and deserves to be corrected in a proper Answer. Comments do not provide enough space to do this. – Stephen C May 11 '11 at 03:44
  • `final` keyword also ensures complete initialization of objects before its read. – amarnath harish Apr 26 '18 at 12:35
  • Yes, but the question is about DCL. How the singleton object is initialized is orthogonal. – Stephen C Apr 26 '18 at 13:04
3

This wikipedia article covers java, c++ and .net (c#/vb) http://en.wikipedia.org/wiki/Double-checked_locking

denis.solonenko
  • 11,645
  • 2
  • 28
  • 23
  • I looked at the wikipedia article wrt C#, and I think that there is a flaw in the version that uses a memory barrier. Specifically, if a thread is preempted *between* assigning `mySingleton` and the barrier, isn't it possible that a second thread will see an instance of `MySingleton` whose fields have not been flushed due to out-of-order memory writes? – Stephen C May 11 '11 at 03:55
  • Another problem with the wikipedia article (as an answer to this question) is that it only talks about Visual C++ ... not about C++ in general. – Stephen C May 11 '11 at 03:59
  • what exactly do they mean by the bolded words in: "the C# keyword volatile can be used to enforce read/write fences around all access of mySingleton, which would **negate many of the efficiencies** inherent in the double-checked locking strategy." – Pacerier May 11 '11 at 06:23
  • @Stephen C do you happen to know what exactly do they mean by the bolded words in: "the C# keyword volatile can be used to enforce read/write fences around all access of mySingleton, which would **negate many of the efficiencies** inherent in the double-checked locking strategy." – Pacerier May 11 '11 at 06:24
  • @Pacerier: The Wikipedia article seems to assume that marking a field volatile incurs extra overhead due to fence instructions. However, on x86/x64, fence instructions are not necessary to implement volatiles (and will not in fact be emitted by the JIT). Volatile fields do have an extra cost on non-x86 architectures (e.g., Itanium). – Igor ostrovsky May 11 '11 at 06:56
  • @Igor ostrovsky: There isn't a need for memory fences to implement `volatile` in the C/C++ sense, but different languages add extra semantics to the operation. VS in C++ adds memory fences around access to `volatile` variables, that is, they have changed the semantics from *has to go to memory in all accesses* to add an extra *and accesses are surrounded by memory fences*. I believe that both Java >= 1.5 and C# use this semantics with `volatile`: The JIT must ensure that there are appropriate memory fences around access to `volatile` variables, regardless of the hardware requirements. – David Rodríguez - dribeas May 11 '11 at 07:31
  • @David Rodríguez - dribeas: C# definitely **does not** insert fences on x86 and x64 for volatiles. I looked at the JIT-ed assembly code a number of times myself. Java 1.5, on the other hand, has stronger volatiles and **does** in fact insert a fence instruction after every volatile write on X86/X64. – Igor ostrovsky May 11 '11 at 08:26
  • @Pacerier - I think it means that the author thinks that C# `volatile` is expensive. But I'm not convinced that is true. (Or particularly relevant ... ...) – Stephen C May 11 '11 at 11:13
3

This is a tricky question, with a mine-field of contradictory information out there.

A part of the problem is that there are a few variants of double-checked locking:

  • The field checked on the fast path may be volatile or not.
  • There is a one-field variant and a two-field variant of double-checked locking.

And not only that, different authors have a different definition for what it means that the pattern is "correct".

  • Definition #1: A widely accepted specification of the programming language (e.g. ECMA for C#) guarantees that the pattern is correct.
  • Definition #2: The pattern works in practice on a particular architecture (typically x86).

As disagreeable as it might seem, a lot of code out there depends on Definition #2.

Let's take C# as an example. In C#, the double-checked pattern (as typically implemented) is correct according to Definition #1 if and only if the field is volatile. But if we consider Definition #2, pretty much all variants are correct on X86 (i.e., happen to work), even if the field is non-volatile. On Itanium, the one-field variant happens to work if the field is non-volatile, but not the two-field variant.

The unfortunate consequence is that you'll find articles making clearly contradictory statements on the correctness of this pattern.

Igor ostrovsky
  • 7,282
  • 2
  • 29
  • 28
2

As others have said, this idiom has had its time. FWIW, for lazy initialization, .Net now provides a built-in class: System.Lazy<T> (msdn). Don't know if something similar is available in java though.

jeroenh
  • 26,362
  • 10
  • 73
  • 104
1

It was flawed in Java, it was fixed in Java 5. The fact that is was broken was more of an implementation issue coupled with a misunderstanding than a technically "bad idea".

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 2
    actually my question is does it also apply to other languages (C#, Vb, C++, etc) – Pacerier May 11 '11 at 03:23
  • Actually, if you don't want Java related answers you shouldn't tag the question with 'java'. – Stephen C May 11 '11 at 03:31
  • I know, but a bad implementation can creep into any language. Other languages are at risk for bad implementations, but even if C#, Vb, C++, etc. had a bad implementation, it would be very hard to call it the "same" bug. I mean, some of these items don't even use Virtual Machines! – Edwin Buck May 11 '11 at 03:33
  • Umm ... in Java 5 and later, DCL is "fixed" only if you declare the instance variable to be `volatile`. What JSR-133 does to make the DCL idiom work is to tie down the specification for `volatile`. – Stephen C May 11 '11 at 04:36
  • Umm... In Java 1 and later, constants are "fixed" only if you use immutable data structures and a `final` variable. Whether there's a JSR in the mix or not, there's a right way to do it, and a million wrong ways. The issue was that for a time there was no right way to do it because of a misperception coupled with an implementation issue, which is now fixed (if you do the "right" thing). – Edwin Buck May 11 '11 at 14:11