Monday, May 20, 2013

What is Double Checked Locking Problem in Multi-Threading ?



Double-Checked Locking Problem
In earlier times (prior to JDK 1.6) a simple uncontended synchronization block was expensive and that lead many people to write double-checked locking to write lazy initialization code. The double-checked locking idiom tries to improve performance by avoiding synchronization over the common code path after the helper is allocated. But the DCL never worked because of the limitations of pervious JMM. 

This is now fixed by new JMM (JDK 1.5 onwards) using volatile keyword.
public class Singleton {
    private Singleton() {}
    private static Singleton instance_ = null; == > A global static variable that will hold the state
    public static Singleton instance() {
        if (instance_ == null){ == > un - synchronized access to this fields may see partially constructed objects because of instruction reordering by the compiler or the cache
            synchronized(Singleton.class) {
                if (instance_ == null)
                    instance_ = new Singleton();
            }
        }
        return instance_;
    }
}
JMM will not guarantee the expected execution of this static singleton.


Why above code idiom is broken in current JMM ?
DCL relies on the un synchronized use of _instance field. This appears harmless, but it is not. Suppose Thread A is inside sycnhronized block and it is creating new Singleton instance and assigning to _instance variable, while thread B is just entering the getInstance() method. Consider the effect on memory of this initialization. Memory for the new Singleton object will be allocated; the constructor for Singleton will be called, initializing the member fields of the new object; and the field resource of SomeClass will be assigned a reference to the newly created object. There could be two scenarios now


• Suppose Thread A has completed initialization of _instance and exits synchronized block as thread B enters getInstance(). By this time, the _instance is fully initalized and Thread A has flushed its local memory to main memory (write barriers). Singleton's member fields may refer other objects stored in memory which will also be flushed out.. While Thread B may see a valid reference to the newly created _instance, but because it didn't perform a read barrier, it could still see stale values of _instance's member fields.

• Since thread B is not executing inside a synchronized block, it may see these memory operations in a different order than the one thread A executes. It could be the case that B sees these events in the following order (and the compiler is also free to reorder the instructions like this): allocate memory, assign reference to resource, call constructor. Suppose thread B comes along after the memory has been allocated and the resource field is set, but before the constructor is called. It sees that resource is not null, skips the synchronized block, and returns a reference to a partially constructed Resource! Needless to say, the result is neither expected nor desired.


Fixed double-checked Locking using volatile in new JMM (multi-threaded singleton pattern JDK 1.5)

The following code makes the helper volatile so as to stop the instruction reordering. This code will work with JDK 1.5 onwards only.
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {

            synchronized(this) {
                if (helper == null)
                    helper = new Helper();
            }
        }
        return helper;
    }

}
If Helper is an immutable object, such that all of the fields of Helper are final, then double-checked locking will work without having to use volatile fields. The idea is that a reference to an immutable object (such as a String or an Integer) should behave in much the same way as an int or float; reading and writing references to immutable objects are atomic.


Alternatives to DCL2
Now a days JVM is much smarter and the relative expense of synchronized block over volatile is very less, so it does not really make sense to use DCL for performance reasons. The easiest way to avoid DCL is to avoid it. We can make the whole method synchronized instead of making the code block synchronized.
Another option is to use eager initialization instead of lazy initialization by assigning at the creation time Here is the example demonstrating eager initialization


class MySingleton {
    public static Resource resource = new Resource();
}


Using Initialization On Demand Holder idiom

Inner classes are not loaded until they are referenced. This fact can be used to utilize inner classes for lazy initialization as shown below

public class Something {
    private Something() {}
    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }
    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

And finally using Enum for Thread-Safe Singleton

public enum Singleton{
    INSTANCE;
}

1 comment:

Your comment will be published after review from moderator