The dreaded double checked locking idiom in Java

The issue discussed in this article is not new, but still tricky even for seasoned developers. The singleton pattern is a common programming idiom. Nevertheless when used with multiple threads, some type of synchronization must be done in order not to break the code.

In a relevant article our JCG partner Manoj Khangaonkar from The Khangaonkar Report examines the double-checked locking idiom in detail to understand just where it breaks down and presents all possible solutions :

Lest see what he has to say :

The problem with double check locking in java is well documented. Yet even a seasoned programmer can get overzealous trying to optimize synchronization of code that creates singletons and fall prey to the trap.

Consider the code

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      s = new Sample() ;
    }
  return s ;
  }
}
Listing 1

This code is not thread safe. If 2 threads t1 and t2 enter the getSample() method at the same time, they are likely to get different instances of sample. This can be fixed easily by adding the synchronized keyword to the getSample() method.

public class Sample {
  private static Sample s = null ;
  public static synchronized Sample getSample() {
    if (s == null) {
      s = new Sample() ;
    }
    return s ;
  }
}
Listing 2

Now the getSample method works correctly. Before entering the getSample method, thread t1 accquires a lock. Any other thread t2 that needs to enter the method will block until t1 exits the method and releases the lock. Code works. Life is good. This is where the smart programmer if not careful, can outsmart himself. He will notice that in reality only the first call to getSample, which creates the instance, needs to be synchronized and subsequent calls that merely return s are paying an unnecessary penalty. He decides to optimize the code to

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      synchronized(Sample.class) {
        s = new Sample() ;
      }
    }
    return s ;
  }
}
Listing 3

Our java guru quickly realizes that this code has the same problem that listing 1 has. So he fine tunes it further.

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      synchronized(Sample.class) {
        if (s == null) {
          s = new Sample() ;
        }
      }
    }
    return s ;
  }
}
Listing 4

By adding an additional check withing the synchronized block, he has ensured that only one thread will ever create an instance of the sample. This is the double check pattern. Our guru’s friend, a java expert, buddy reviews the code. Code is checked in and product is shipped. Life is good right ?

Wrong !! Let us say thread t1 enters getSample. s is null. It gets a lock. Within the synchronized block, it checks that s is still null and then executes the constructor for Sample. Before the execution of the constructor completes t1 is swapped out and t2 gets control. Since the constructor did not complete, s is partially initialized. It is not null, but has some corrupt or incomplete value. When t2 enters getSample, it sees that s is not null and returns a corrupt value.

In summary, the double check pattern does not work. The options are to synchronize at a method level as in listing 2 or to forego synchronization and use a static field as shown below.

public class Sample {
  private static Sample INSTANCE = new Sample();

  public static Sample getSample()  {
    return INSTANCE ;
  }
}
Listing 5

Better is the enemy of good!

Byron

Related Articles:

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

One Response to "The dreaded double checked locking idiom in Java"

  1. SlugFiller says:

    The claim in this post is incorrect. It assumes that “s” may contain a partially initialized version of “Sample”. This shows a distinct misunderstanding of how this line is executed:
    s = new Sample();

    It ignores the “right-first” principle of assignment – The right hand is fully evaluated before the left hand.

    In this case, first “new Sample()” is evaluated, meaning the instance is created and FULLY initialized. Only after this process has completely finished, is the FINAL, fully initialized version of the instance assigned to the static variable “s”.

    This could still be a problem if the initialization code required multiple lines, as so:
    s = new Sample();
    s.doSomeInit(someConstant);

    This has two solutions:
    1. Do all the initialization in the constructor
    2. Use a temporary local variable before assigning to the static variable:
    Sample temp = new Sample();
    temp.doSomeInit(someConstant);
    s = temp;

    Small aside: It should be noted that in the standard VM, the “risk” doesn’t exist at all. Since “s” isn’t declared as volatile, each thread has its own version of “s” which is copied upon entering a synchronized block. This means that even if “s” could contain a partial value, it would still appear as “null” to any other thread until that thread has synchronized on -something-.

    In fact, even if using the double check pattern, most threads would initially go through both checks at least once, since until entering the synchronized they would each hold their own copy of “s” which is still set to “null”, long after the first thread to call the function has exited the synchronized block.

    In other words, the optimization doesn’t apply to the first call to this function from each given thread. But it does apply if each thread makes several calls to the function.

Leave a Reply


6 × seven =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books