Biased Locking: JDK 1.6 introduced biased locking to optimize uncontended synchronization. In JDK 1.8, biased locking has been further improved to reduce the overhead of uncontended locks. Biased locking allows a lock to be biased toward a particular thread, making the lock acquisition faster for that thread if there is no contention.
Lightweight Contention: JDK 1.8 improved the performance of contended locks by using adaptive spinning and biased locking. It optimizes the lock acquisition process by using spin-waiting (a busy-wait loop) before actually putting the thread to sleep, which reduces context switching and improves the overall performance of contended locks.
Improvements in Synchronization: JDK 1.8 introduced various optimizations in the synchronization mechanisms to make it more efficient for small critical sections, often referred to as "thin locks" or "inflated locks."
G1 Garbage Collector Improvements: JDK 1.8 introduced the Garbage-First (G1) garbage collector, which performs better in situations with high contention and high allocation rates.
Hash-based Synchronization for Some Data Structures: JDK 1.8 replaced some internal data structures with hash-based synchronization, which improves performance when multiple threads access data structures concurrently.
Various Compiler Optimizations: The JIT (Just-In-Time) compiler in JDK 1.8 has undergone several improvements, including better lock elision, loop optimizations, and inlining, which can lead to improved performance for code that uses locks.
Overall, JDK 1.8 has brought significant optimizations and improvements to the performance of pessimistic locking, making it more efficient and scalable for multi-threaded applications. However, it's essential to note that the actual performance benefits may vary depending on the specific use case and the design of the application. As always, it's best to benchmark and profile the application to identify areas where locking can be optimized and improved.