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.