This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

Why introduce lock escalation

Prior to JDK1.6, synchronized was a built-in heavyweight lock in Java. In order to suspend and wake up threads from user mode to kernel mode, frequent switching from user mode to kernel mode was CPU intensive. We all call them synchronized.

After JDK1.6, the concept of synchronized was introduced. Instead of being a heavyweight lock, synchronized gradually moves from non-locks, partial locks, lightweight locks, and heavyweight locks, depending on the concurrency of the threads, in an effort to acquire the locks at the minimum cost. It is important to note that locks can only be upgraded, not degraded. The purpose of this design is to increase the efficiency of lock acquisition and lock release. There is a price to be paid for upgrading locks.

In fact, those who have done the development know that in our development, the locks used can be roughly divided into two kinds, one is called optimistic lock, the other is called pessimistic lock, because in different scenarios, using different kinds of locks can improve the efficiency of our program execution and reduce certain resource consumption.

For example, optimistic locking is less expensive than pessimistic locking when concurrency is low, but pessimistic locking is less efficient than optimistic locking when concurrency is high.

Optimistic locking is usually implemented by CAS+ spin mechanism. When concurrency is high, some threads may spin all the time because they have not acquired the lock, doing a lot of meaningless CPU idling, which is not as effective as suspending the thread directly. So, with that in mind, the JDK optimizes Synchronized in this way.

Several states of an object lock

Synchronized locks are associated with an object, which is the Class object of a static method, this object of a normal method, and the object we pass in to a block of code. So how does this associated object represent what the current lock is? The answer is actually in the object header of the object. For articles on object structure, refer to the Java Object structure

Unlocked state

When a Java object is first created, no threads are competing with it, which means the object is in an unlocked state (no threads are competing with it). The biased lock flag bit is 0 and the lock state is 01

Deviation lock state

In the biased state, the object’s markWord stores the Thread ID of the biased Thread. Note that the Thread ID here is not the one we used to access through thread.currentThreadid (), It is a built-in thread ID allocated by the operating system. When a biased thread executes a synchronized block of code, it only needs to determine whether the thread ID stored in the object header is the current thread ID, so the cost is very small.

Lightweight lock

When two threads start competing for the lock object, the situation changes. Instead of a biased lock, the lock is upgraded to a lightweight lock, and the two threads compete fairly. That thread first owns the lock object, and the MarkWord of the lock object points to the lock record of that thread’s stack frame.

When a lock is in the biased state and another thread wants to acquire the lock, the biased lock is upgraded to a lightweight lock, and the thread attempting to acquire the lock spins to acquire the lock. The JVM does not block the thread preempting the lock, avoiding the cost of the user thread switching from user mode to kernel mode.

Spin is consumption of CPU resources, has been getting less than lock, thread CPU spin will always do no meaning, so, need to set a maximum spin waiting time, the JVM introduces adaptive spinning, the spin of the time is not fixed, but has the last thread spin on the lock time, and decided to lock the owner of the state, if the thread spin is successful, The next spin will have more words, and if the spin fails, the next spin will have fewer words.

Heavyweight lock

If the thread holding the lock time more than the maximum waiting time is still not release the lock, it can lead to other threads are waiting for acquiring a lock on the maximum waiting time will never get a lock, at this point, the spin will not last forever, at this moment, the JVM will stop the spin thread, will participate in the spin thread hangs, micro expansion heavyweight lock lock.

A heavy lock causes other requesting threads to block directly, because it involves the user thread switching from user mode to kernel mode, so performance is degradedSynchronization lockOf the lock objectMarkWordIt points to a monitorMonitorObject that registers and manages queued threads as a collection.