Here's the dilemma: with multiprocessor machines the norm these days we need to write concurrent applications to take advantage of multiprocessing; but atomic operations that are the building blocks of concurrency controls needed in these applications are significantly more expensive on multiprocessor computers. To address this issue the folks at Sun have implemented a runtime optimization called Biased Locking in Java 6. In their own words,
Biased Locking is a class of optimizations that improves uncontended synchronization performance by eliminating atomic operations associated with the Java language’s synchronization primitives. These optimizations rely on the property that not only are most monitors uncontended, they are locked by at most one thread during their lifetime.
For the gritty details you can read "Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing".
The first level of optimization involves so-called lightweight locking, which avoids heavier operating system mutexes and conditional variables. Lightweight locking instead uses atomic operations where possible, before resorting to the aforementioned heavyweight operations. This works well because most lock acquisitions in real world applications are uncontended and atomic operations suffice.
The second level of optimization, Biased Locking, involves avoiding atomic operations where possible. In many real world applications locks are acquired repeatedly by the same thread. This makes it possible to bias the lock toward a thread, allowing that thread to acquire the lock without atomic operations. The HotSpot JVM implements Store-Free Biased Locking, which I understand as not storing anything to the object header when a lock is biased to the current thread.
Biased Locking performs well until bias revocation is required (when another thread attempts to acquire the lock). Even if there is no contention the bias must be revoked, an expensive operation. This occurs, for example, in the common producer-consumer pattern. Biased locking in producer-consumer queues would be disadvantageous because of all the revocations that would take place as the two threads work off the queue.
To deal with such scenarios the Sun JVM performs bulk rebiasing and bulk revocation, followed finally by disabling SFBL. The rebiasing and revocation are done in bulk on all instances of the data type. This amortizes the cost of the rebiasing and revocation. Performing the bulk operations on the data type has been shown to be reasonable. This intuitively makes sense, since each class will probably be used in the same fashion. Heuristics are used to decide when to bulk rebias, then when to bulk revoke and disable SFBL.
So there you have it, you can have you cake and eat it too. Continue to write those multithreaded applications, run them on mutiprocessor servers, and let the JVM optimize the runtime synchronization.
Comments