I recently came across the following post on the Resharper website. It was a discussion of double-checked locking, and had the following code:
public class Foo
{
private static volatile Foo instance;
private static readonly object padlock = new object();
public static Foo GetValue()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Foo();
instance.Init();
}
}
}
return instance;
}
private void Init()
{
...
}
}
The post then makes the claim that
If we assume that Init() is a method used to intialize the state of Foo, then the above code may not function as expected due to the memory model not guaranteeing the order of reads and writes. As a result, the call to Init() may actually occur before the variable instance is in a consistent state.
Here are my questions:
It was my understanding that .NET memory model (at least since 2.0) has not required that instance
be declared as volatile
, since lock
would provide a full memory fence. Is that not the case, or was I misinformed?
Isn't read/write reordering only observable with respect to multiple threads? It was my understanding that on a single thread, the side effects would be in a consistent order, and that the lock
in place would prevent any other thread from observing something to be amiss. Am I off-base here as well?
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.
Since only the first access requires locking, double-checked locking is used to avoid locking overhead of subsequent accesses. However, on many languages and hardware, the design can be unsafe.
In software engineering, double-checked locking (also known as "double-checked locking optimization") is a software design pattern used to reduce the overhead of acquiring a lock by testing the locking criterion (the "lock hint") before acquiring the lock.
No matter how you rig it, double-checked locking still fails Unfortunately, DCL isn't guaranteed to work under the current Java Memory Model (JMM.)
The big problem with the example is that the first null check is not locked, so instance may not be null, but before Init has been called. This may lead to threads using instance before Init has been called.
The correct version should therefore be:
public static Foo GetValue()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
var foo = new Foo();
foo.Init();
instance = foo;
}
}
}
return instance;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With