What is Double Checked Locking Problem in Multi-Threading?

Upasana | May 05, 2019 | 4 min read | 229 views


Double-Checked Locking Problem

In earlier times (prior to JDK 1.6) a simple un-contended 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 previous JMM.

Not Thread safe: JMM will not guarantee the expected execution of this static singleton.
public class Singleton {
    private Singleton() {}
    private static Singleton instance_ = null; (1)

    public static Singleton instance() {
        if (instance_ == null) {     (2)
            synchronized(Singleton.class) {
                if (instance_ == null)
                    instance_ = new Singleton();
            }
        }
        return instance_;
    }
}
1 A global static variable that will hold the state
2 unsynchronized access to this fields may see partially constructed objects because of instruction reordering by the compiler or the cache

This is now fixed by new JMM (JDK 1.5 onwards) using volatile keyword.

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 synchronized 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

  1. Suppose Thread A has completed initialization of _instance and exits synchronized block as thread B enters getInstance(). By this time, the _instance is fully initialized 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.

  2. 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.

Thread-safe version using volatile
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.

Better Alternatives to DCL

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

Eager instantiation using static keyword

Eagerly instantiated singleton
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

On demand holder idiom for thread-safe Singelton
public class Something {
    private Something() {}
    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }
    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

Enum for Thread-Safe

And finally we can use Enum developing for Thread-Safe Singleton class.

Thread-safe singleton using enum
public enum Singleton{
    INSTANCE;
}

Top articles in this category:
  1. Producer Consumer Problem using Blocking Queue in Java
  2. What will happen if we don't synchronize getters/accessors of a shared mutable object in multi-threaded applications
  3. What is Immutable Class in Java
  4. Troubleshooting Deadlock in Java
  5. What is purpose of Collections.unmodifiableCollection
  6. What is difference between sleep() and wait() method in Java?
  7. Difference between Callable and Runnable Interface

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.