In previous releases,synchronized was labeled as a high performance and heavy lock, and was given the name heavyweight lock, but a wave of optimizations since jdk1.6 have made it less heavy, so that synchronized can now be used without it There are particular concerns about its performance drain

Optimization of synchronized locks

First of all, let’s make a simple analogy. Let’s compare the locked code to a room, where there is only one key. Each thread that enters the code block represents the person who enters the room.

Biased locking

After the first person to enter A room, the administrator didn’t put the key away, but the key on the door frame, and tell this people, as long as it is after you get the door directly, so if the A use this room, so he won’t need to get the key to administrator every time, go directly to take key frame, this is biased locking.

Biased locking, as the name implies, is biased toward a certain thread lock, JDK developers found that although the user to lock the code, but most of the time a separate thread access, if this time to manually acquire the lock, then the performance is very expensive.

Therefore, when a thread successfully locks for the first time, the preferred thread ID will be stored in the lock record of the object header and the thread’s stack frame. If the thread acquires the lock again next time, cas operation is not required to lock and unlock, which greatly reduces the consumption of performance.

Biased locks work only if one and only one thread acquires the lock, and if a second thread tries to acquire the lock, biased locks inflate to lightweight locks

Lightweight lock

Let’s continue using the above metaphor. If B comes to the room and finds that the room has already been opened, he will look for the key on the door frame. If he finds the key, he doesn’t need to go to the administrator and opens the door directly with the key, saving the cost of interaction with the administrator.

Compared with heavyweight locks, lightweight locks do not need to apply for mutex, but only need to update some bytes of CAS in Markwork to the thread ID. If the update is successful, it means that the lock has been successfully acquired; otherwise, it means that some threads have acquired the lightweight lock, and lock competition occurs, and the lightweight lock starts to spin.

Spin locks and adaptive spin locks

Metaphor to continue, during B use the room, A also to want to enter the room, he is coming later found the key not on the door frame, when he is not directly go to the administrator, but in the doorway, around A few times if the B used up the room, you can continue to use in A, if B good half-day also not to come out, or again A C at this time, it went to A queue (administrator Bulges to heavyweight lock)

Spin lock is made of lightweight lock before lock expansion last struggle, because some code even if the lock, but execution efficiency, fast or competitive rate is very low, the competition of the thread is not necessary to block off, only need to loop (for example, the for loop) wait for a short period of time, on a thread lock release, in 1.5 JDK set spinlocks to spin, ten times throughout the year Optimized for adaptive spin lock in 1.6, you can select the number of times according to the lock code. If the spin exceeds a certain number, or if a third thread is competing for the lock, the lock expands to a heavyweight lock

Heavyweight lock

The concrete implementation of synchronized is shown in the following figure

Synchronized is an unfair lock. When a thread attempts to spin the lock before entering the tail queue, it will choose to enter the tail queue if the lock fails.

Explanation of other lock nouns

Fair locks and unfair locks

As the name implies, fair lock refers to the first acquisition lock of the advanced queue, which is very fair, while non-fair lock is likely to not enter the queue, directly acquire the lock and jump the queue like synchronized.

Optimistic and pessimistic locks

Optimistic lock is an optimistic idea, that is, read more write less, the possibility of concurrent write is relatively low, each time the data is not returned to the home, during the update according to the version number to determine whether others have changed the data, if someone has changed the data, repeat read – compare – write operation

Pessimistic locking is the pessimistic thinking that if you write too much and read too little, you get locked every time you get data, and any other data you read will block until you get the lock

Reentrant lock

ReentrantLock, also known as recursive lock, means that when the outer method of the same thread acquires the lock, the inner method of the same thread automatically acquires the lock (provided that the lock is the same object or class). It will not be blocked because the lock has been acquired before and has not been released. Synchronized and ReentrantLock in Java are both synchronized and ReentrantLock Reentrant locks can avoid deadlocks in some cases

Reentrant locks and non-reentrant locks both inherit from the parent AQS class. The implementation of AQS will be explained in a separate chapter. In the parent AQS class, a synchronization status is maintained to count the number of hits

The difference between a reentrant lock and a non-reentrant lock is that a reentrant lock runs on status! If =0, the thread will detect whether it has acquired the lock of the object. If so, the status will be +1. Release is also released layer by layer until status=0

A non-reentrant lock blocks as long as the status is not equal to 0 regardless of who acquires the lock. If the thread acquires the lock itself and does not live for a timeout, a deadlock occurs

Exclusive locks and shared locks

An exclusive lock can be used by only one thread at a time, while a shared lock can be held by multiple threads.

Mutex and read-write locks

The exclusive and shared locks mentioned above are general concepts, while mutex and read-write locks are concrete implementations

In Java, the mutex is implemented as ReentrantLock, and the read/write lock is ReadWriteLock

Mutex can be acquired by only one thread at a time, while read/write locks can be concurrently executed while read/write locks are mutually exclusive

Thank you for reading

If you are interested, you can follow my personal wechat public account. I will regularly push technical articles about Java, and at present, they are all dry goods