![]() |
Michael Gilfix Online |
Navigation |
Double-Checked Locking in JavaDouble checked locking is a useful paradigm for avoiding the cost of unnecessary synchronization and is often used in the context of avoiding synchronization during lazy initialization. The basic idea behind double checked locking is that you check variable condition first, and if the condition is not met, then synchronize, perform the check again, and adjust the condition. The second check inside the synchronized block typically serves to avoid repeating the actions that were used to satisfy the condition in the first time. There are quite a few articles on the web on this topic. However, some are incorrect, provide only partial information, or are quite complicated. This is an older, but enduring topic. So here's an attempt to distill it down. The basic problem that double checked locking is trying to alleviate are conditions like this:
Here, synchronization needs to occur for every fetching of an instance. This same pattern applies to any other lazy execution of static initialization that is undesirable to be repeated. The often cited and incorrect solution to this problem is code that looks as follows. This code implements a double-check locking approach to avoid synchronization of the typical execution path:
This code is broken, because the JVM does not provide atomicity guarantees on non-primitive 32-bit values such as object references, and 64-bit values such as longs or doubles. However, the JVM memory model does provide atomicity for 32-bit primitives, such as ints, floats, or booleans. Double-checked locking will work with these. So, you should be able to write the code above as follows:
This approach works on hardware architectures like Intel, Solaris, etc. All these platforms have stronger memory model guarantees than Java's. However, there is still a theoretical concern that some future processor architecture may not provide sufficient read coherency to support this memory model. This is discussed here.. Now, as of JSR133, which you should definitely have if you're running on Java 1.5, you should be able to use the volatile keyword to achieve this more simply. In addition, the adjustment to the Java memory model should fix any of the theoretical doubts of the approach above.
The volatile keyword causes writes/reads to go directly to main memory, rather than the cache. This avoids the cache coherency problem that prevented the double checked locking from working on multi-processor systems. While this approach fixes the double locking technique to work with instances, it still comes with some overhead: the value of the instance variable can no longer be cached and must be fetched from memory every time. This is similar in cost to a partial synchronization. Of course, the fastest thing is just to do static eager initialization, which gets run once in a single thread by the class loader when loading the class. However, this approach may not always be possible, particularly in an application server environment where certain resources may need to be bound to at runtime. Of course, you can't go wrong with synchronization. But in many casts, this technique can be used to achieve cost savings for critical code paths when run on the usual platforms. By Michael Gilfix at 2007-03-16 21:03 | Code | Java | Michael Gilfix's blog | login or register to post comments
|
SearchRecent blog posts
|