Upgrade of java concurrency lock

Note: Most of the content of this article comes from "The Art of Concurrent Programming", plus my own network organization and understanding.
The following content comes from "The Art of Java Concurrent Programming". Author: Fang Pengfei, Wei Peng, Cheng Xiaoming

Synchronized has always been a veteran role in multi-threaded concurrent programming, and many people will call it a heavyweight lock. However, with Java SE 1.6 various optimizations have been made to synchronized, in some cases it is not so heavy.

Lock upgrade and comparison

In order to reduce the performance consumption caused by acquiring and releasing locks, Java SE 1.6 introduced "biased locks" and "lightweight locks". In Java SE 1.6, there are 4 lock states, and the levels from low to high are : No lock state, biased lock state, lightweight lock state, and heavyweight lock state. These states will gradually escalate with competition. Locks can be upgraded but not downgraded, which means that after a biased lock is upgraded to a lightweight lock, it cannot be downgraded to a biased lock. The purpose of this lock upgrade but not downgrade strategy is to improve the efficiency of acquiring and releasing locks, which will be analyzed in detail below.

markword

Because of biased locking, when an object is locked, the corresponding identifier of the object header will be written. We first illustrate the object header (official name: Mark Word) as follows (borrowed from a netizen's picture):
Write picture description here

1. Bias lock

The author of HotSpot [1] found through research that in most cases, locks not only do not have multi-thread competition, but are always acquired multiple times by the same thread. In order to make the cost of threads acquiring locks lower, biased locks were introduced. When a thread accesses a synchronized block and acquires a lock, the lock-biased thread ID is stored in the object header and the lock record in the stack frame. In the future, the thread does not need to perform CAS operations to lock and unlock when entering and exiting the synchronized block. , just simply test whether there is a bias lock pointing to the current thread stored in the Mark Word of the object header. If the test succeeds, it means that the thread has obtained the lock. If the test fails, you need to test again whether the bias lock flag in Mark Word is set to 1 (indicating that it is currently a bias lock): if it is not set, use CAS to compete for the lock; if it is set, try to use CAS to change the object header The bias lock points to the current thread .

The boldfaced part in the above article is written too briefly, so many beginners are a little confused about this process. How does this process realize the upgrade and release of the lock? Analyze one by one below

  1. Thread 2 to compete for the lock object;
  2. Determine whether the current object header is a biased lock;
  3. Determine whether thread 1 with a biased lock still exists;
  4. Thread 1 does not exist, directly set the bias lock flag to 0 (after thread 1 is executed, it will not actively release the bias lock);
  5. Use cas to replace the biased lock thread ID as thread 2, the lock is not upgraded, and it is still a biased lock;
  6. Thread 1 still exists, suspend thread 1;
  7. Set the lock flag to 00 (becoming a lightweight lock), and the bias lock to 0;
  8. Read one item from the idle monitor record of thread 1 and put it into the current monitor record of thread 1;
  9. Update the mark word and point the mark word to the pointer of the monitor record in thread 1;
  10. Continue to execute the code of thread 1;
  11. The lock is upgraded to a lightweight lock;
  12. Thread 2 spins to acquire the lock object;
    Bias lock contention process and upgrade process

2. Lightweight lock

(1) Before the lightweight lock locking
thread executes the synchronization block, the JVM will first create a space for storing the lock record in the stack frame of the current thread, and copy the Mark Word in the object header to the lock record. Official Called Displaced Mark Word. Then the thread tries to use CAS to replace the Mark Word in the object header with a pointer to the lock record. If it succeeds, the current thread acquires the lock. If it fails, it means that other threads compete for the lock, and the current thread tries to use spin to acquire the lock.
(2) Lightweight lock unlocking:
During lightweight unlocking, the Displaced Mark Word will be replaced back to the object header using an atomic CAS operation. If successful, it means that no competition occurs. If it fails, it means that the current lock is in competition, and the lock will expand into a heavyweight lock. The figure below is a flow chart in which two threads compete for the lock at the same time, resulting in lock expansion.
Lightweight lock and expansion
Because spin consumes CPU, in order to avoid useless spin (for example, the thread acquiring the lock is blocked), once the lock is upgraded to a heavyweight lock, it will not be restored to the lightweight lock state. When the lock is in this state, other threads will be blocked when trying to acquire the lock. When the thread holding the lock releases the lock, these threads will be awakened, and the awakened threads will engage in a new round of competition for the lock.

Guess you like

Origin blog.csdn.net/suyuaidan/article/details/80738066