Double Checked Locking in Java and Why it was broken before JDK 5?

Double checked locking pattern is one of the interesting topics on Java Interviews. Earlier, it was asked to see if Java developer can write code using synchronized block or not and now it ask to gauge the candidate's understanding of concurrency, volatile and synchronization in Java. One of the simplest ways to write thread-safe Singleton was to make the getInstance() method synchronized but prior to JDK 1.6, a simple uncontented synchronization block was expensive and that lead many developers to write the getInstance() method of Singleton class using double-checked locking idiom. This was one of the clever idiom of that time which only uses synchronization when the Singleton object is created as seen in the following code and thus improves the performance of getInstance() method, which is used to retrieve the Singleton object.

In this article, we will learn quite many things about Double checked locking pattern e.g. how to code it, how it work, benefits and shortcomings of this pattern etc. But, let's first see the code.

Here is one example of a thread-safe Singleton class in Java using double checked locking pattern:

class DCLSingleton {

    private static voaltile DCLSingleton _instance  = null;

    private DCLSingleton() {
    }

    public static DCLSingleton instance() {
        if (_instance == null) { // 1st check

            synchronized (DCLSingleton.class) {

                if (_instance == null) // 2nd check
                {
                    _instance = new DCLSingleton();
                }
            }
        }
        return _instance;
    }
}

There are couple of important points to note about this code:

1) A static volatile field is used to hold the instance of Singelton class. The volatile variable is key here, without volatile, it won't be thread-safe as explained in Java Concurrency in Practice as well.

Double checked locking in Singleton Java


2) The constructor is made private to disable instance creation outside of this class, but you can create instance inside the class and that's what your getInstance() method does.

3) There are two checks to see if _instance member variable is initialized or not and that's why this code idiom it's called double-checked locking idiom.

4) The first check is non-synchronized, which may see a partially constructed object because of instruction re-ordering by the compiler or JVM.

5) Second check is inside synchronized blcok and only execute one time during lifespan of Singleton. That's why you get the performance boost becuase locking only happen one time during lifespan of Singleton instance.




Why Double checked locking is broken Prior to 5

The sole purpose of Double checked locking was to avoid excessive synchronization and hence it relies on non-synchronized access of _instance field at the 1st check point. This appears harmless, but it is not.

Suppose one thread, Thread-1 is inside synchronized block and it's creating Singleton instance and assigning reference to _instance variable. In the mean time, Thread scheduler stops the Thread-1. Now, a second thread, Thread-2 enters and come to 1st check point which is not synchronized, now there is a possibility that it can see half-initialized _instnace field and return that to the client, leading to subtle bugs in your program.

This issue was fixed by introducing happens-before guarantee provided by the volatile variable in Java 1.5. According to this rule, write to a volatile field will happen before any read, which negates the possibility of seeing half initialized instance of Singleton class.

See Java Concurrency in Practice for more details on happens-before guarantee.

double checked locking example in java




Safe alternatives of Double-checked Locking Pattern

If getInstance() method is not bottleneck than simplest way to avoid Double checked locking is make the whole method synchronized. Java has come a long way in performance and penalty to synchronizing is a lot lesser than what it used to be.

Other alternative includes using Enum as Singleton in Java. Enum guarantees lot of features required by Singleton pattern out-of-the-box e.g. initialization to Enum constants are thread-safe, Enum also provides Serialization guarantee that only one instance of Singleton will exits and it's very easy to code as seen below:

public enum ThreadSafeSingleton{
  INSTANCE;
}

Another safe way to create thread-safe Singleton is using eager initialization which initializes Singleton instance at the time Singleton class is loaded into memory as opposed to when client class the getInstance() method. If your Singleton is not very heavy than this idiom works quite well.

class EagerSingleton {
   public static EagerSingleton singleton = new EagerSingleton ();
}


One more alternative of double checked locking is Initialization on Demand Holder idiom, which uses an inner class to encapsulate Singleton instance. This idiom takes advantage of the fact that Inner class is not loaded until they are referenced.

You can still use double checked locking pattern to create thread-safe Singleton classes in Java, but when you use double checked locking, don't forget to include the volatile modifer on Singleton instance.

Here is a bad and good example of double checkecd locking with Singleton in Java, following code misses the volatile modifier, hence Singleton is not thread-safe:

Double checked locking without volatile variable

You need to use the volatile variable to make the above code thread-safe in Java 1.5 as shown below:

Double checked locking in Java with volatile variable


That's all about why double checked locking was broken, why you should avoid it and what are some safe alternatives of double-checked locking for creating thread-safe Singleton in Java. Remember, you can use Double checked locking with Java 6, 7 or 8 but don't forget to make the _instance static variable as volatile, without that your Singleton will still be broken.


4 comments:

  1. The volatile keyword always existed in java, so what exactly has changed in 1.5 ?

    ReplyDelete
  2. With jdk 1.5 release, the implementation of volatile keyword also changed which makes it work with 1.5 and later. Refer this: http://singletonjava.blogspot.com/2015/12/java-singleton-design-pattern-double.html

    ReplyDelete