Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Can ThreadLocal solve the double-checked locking problem?

ThreadLocal appears to fix the thread-safety issues behind double-checked locking

  • Print
  • Feedback

Page 3 of 3

Listing 2. DCL using ThreadLocal

class ThreadLocalDCL {
  private static ThreadLocal initHolder = new ThreadLocal();
  private static Resource resource = null;
  public Resource getResource() {
    if (initHolder.get() == null) {
      synchronized {
        if (resource == null) 
          resource = new Resource();
        initHolder.set(Boolean.TRUE);
      }
    }
    return resource;
  }
}


How does this version differ from the classic DCL implementation? Instead of checking to see if the shared resource field is nonnull, we use a ThreadLocal to store the answer to the question "Has this thread been through the synchronized block yet?" The ThreadLocal.get() method is thread-safe, so calling it outside the synchronized block is safe. Since the thread-local operations do not involve sharing data between threads, we have none of the reordering problems that we would have had with a shared initialized variable. The resource field does not get referenced unless the thread has already executed the synchronized block, guaranteeing that if the Resource has been constructed by another thread, it and all the objects it references are visible to the executing thread. And on the common code path, where the Resource has already been initialized and the executing thread has a consistent view of the shared object, no synchronization is required. It appears that this meets the requirements that DCL was intended to address -- lazy initialization without synchronization.

The proof is in the performance

But the real motivation behind DCL was to eliminate the synchronization on the common code path because synchronization is expensive. So to say that this technique "solves" the DCL problem, ThreadLocal would have to be faster than a synchronized lazy initialization like SingleCheckExample. Unfortunately, this is not yet the case.

The initial version of ThreadLocal performed poorly. It was implemented with a synchronized WeakHashMap, using the Thread object as the key. Thus executing ThreadLocal.get() or ThreadLocal.set() required not only synchronization but also de-referencing a weak reference. Needless to say, that would be even slower than SingleCheckExample.

The JDK 1.3, the current JDK as of this writing, features a substantially improved ThreadLocal implementation. The Thread class was modified to provide support for thread-local variables, and the ThreadLocal accessor methods run without synchronization, requiring only a HashMap lookup to find the local thread's variable copy. However, the Thread.currentThread() method, which is used to find the current thread and thus the current thread's thread-local variables, is surprisingly expensive.

Even under JDK 1.3, executing ThreadLocal.get() is about twice as slow as an uncontended synchronization. This means that while ThreadLocalDCL is an elegant and correct replacement for the incorrect DCL idiom, using ThreadLocal to implement DCL is still slower than SingleCheckExample -- and the whole point was to be faster. If it's not faster than synchronizing, we really can't say that ThreadLocal solves the DCL problem.

What about JDK 1.4?

In JDK 1.4, ThreadLocal and Thread.currentThread() have been rewritten again to be much faster. The improved versions were included in the JDK 1.4 Beta 2 release, and informal tests show that ThreadLocal's performance (at least when using the -server JVM option, but not the -client JVM option) is substantially faster than either the previous version or an uncontended synchronization. So once JDK 1.4 becomes widely deployed, ThreadLocal will, in some environments, meet the performance goals of DCL without compromising thread safety.

We're almost out of the woods

The 1.4 (Merlin) JDK is still in beta and, even when released, might not be widely deployed and used for some time. So for the present, using ThreadLocal to solve the DCL problem still falls short of the original goal of DCL -- performance. However, after Merlin is released and more widely deployed, performance problems will no longer hobble ThreadLocal; it will become a practical, efficient, and clean solution to many problems in concurrent programming, not just lazy initialization.

About the author

Brian Goetz is a professional software developer with more than 15 years of experience. He is a principal consultant at Quiotix, a software development and consulting firm located in Los Altos, Calif.
  • Print
  • Feedback

Resources