Newsletter sign-up
View all newsletters

Sign up for our Enterprise Java Newsletter

Enterprise Java

Double-checked locking: Clever, but broken

Do you know what synchronized <em>really</em> means?

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
From the highly regarded Elements of Java Style to the pages of JavaWorld (see Java Tip 67), many well-meaning Java gurus encourage the use of the double-checked locking (DCL) idiom. There's only one problem with it -- this clever-seeming idiom may not work.

Double-checked locking can be hazardous to your code!

This week JavaWorld focuses on the dangers of the double-checked locking idiom. Read more about how this seemingly harmless shortcut can wreak havoc on your code:


What is DCL?

The DCL idiom was designed to support lazy initialization, which occurs when a class defers initialization of an owned object until it is actually needed:

class SomeClass {
  private Resource resource = null;
  public Resource getResource() {
    if (resource == null)
      resource = new Resource();
    return resource;
  }
}


Why would you want to defer initialization? Perhaps creating a Resource is an expensive operation, and users of SomeClass might not actually call getResource() in any given run. In that case, you can avoid creating the Resource entirely. Regardless, the SomeClass object can be created faster if it doesn't have to also create a Resource at construction time. Delaying some initialization operations until a user actually needs their results can help programs start up faster.

What if you try to use SomeClass in a multithreaded application? Then a race condition results: two threads could simultaneously execute the test to see if resource is null and, as a result, initialize resource twice. In a multithreaded environment, you should declare getResource() to be synchronized.

Unfortunately, synchronized methods run much slower -- as much as 100 times slower -- than ordinary unsynchronized methods. One of the motivations for lazy initialization is efficiency, but it appears that in order to achieve faster program startup, you have to accept slower execution time once the program starts. That doesn't sound like a great trade-off.

DCL purports to give us the best of both worlds. Using DCL, the getResource() method would look like this:

class SomeClass {
  private Resource resource = null;
  public Resource getResource() {
    if (resource == null) {
      synchronized {
        if (resource == null) 
          resource = new Resource();
      }
    }
    return resource;
  }
}


After the first call to getResource(), resource is already initialized, which avoids the synchronization hit in the most common code path. DCL also averts the race condition by checking resource a second time inside the synchronized block; that ensures that only one thread will try to initialize resource. DCL seems like a clever optimization -- but it doesn't work.

Meet the Java Memory Model

More accurately, DCL is not guaranteed to work. To understand why, we need to look at the relationship between the JVM and the computer environment on which it runs. In particular, we need to look at the Java Memory Model (JMM), defined in Chapter 17 of the Java Language Specification, by Bill Joy, Guy Steele, James Gosling, and Gilad Bracha (Addison-Wesley, 2000), which details how Java handles the interaction between threads and memory.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comments (11)
Login
Forgot your account info?

Read aboveBy Anonymous on January 25, 2010, 7:32 pmThe links in the above comments show that this won't work in java < 1.5. The violtile memory trick is a java 1.5 thing. Also, adding a layer of method calls...

Reply | Read entire comment

What's broken: DCL or your example?By o.cornu on January 21, 2010, 1:10 pmIf i get your point right, your problem comes from *instantiating* Resource, which makes resource = new Resource(); _not_ an atomic operation (no matter resource...

Reply | Read entire comment

Synchronization no longer so slowBy Anonymous on December 1, 2009, 8:34 amYou need to update the assertion that synchronized methods are up to 100x slower than unsynchronized methods. According to which benchmarks, against which JVM? Synchronization...

Reply | Read entire comment

"While thread B may see a valid reference to the newly created RBy Anonymous on September 30, 2009, 3:12 am"While thread B may see a valid reference to the newly created Resource, because it didn't perform a read barrier, it could still see stale values of resource's...

Reply | Read entire comment

No, your implementation isBy Anonymous on September 11, 2009, 9:52 pmNo, your implementation is not safe. Remember, there is no guarantee that 'foo = new Foo();' will excute before 'h =1;'. If it doesn't, then the second thread might...

Reply | Read entire comment

View all comments

Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • Double-checked locking idiom:
  • The Java Memory Model and multithreaded programming