Detailed explanation of synchronized lock

This article mainly explains the various uses of synchronized and the underlying principles of unlocking.

1. Heavyweight lock

Object header

Before talking about heavyweight locks, let’s first understand the composition of an object. An object is composed of an object header and an object body. This article mainly talks about the object header. The object body is actually the member variable of the object. The object header is composed of mark word and klass word. , the klass word refers to the type of the current object, and the mark word refers to the hashcode of the object in the normal state, the era it is in, whether it is biased locking, and the locked state, see the figure below

monitor (lock) - the underlying implementation of the operating system, not directly controlled by us

Next, we will officially enter the explanation of heavyweight locks. First, each Java object can be associated with a monitor object. As long as we use synchronized, the markwork of the object will be set to change the pointer address of the monitor, and the status will be set to 10, see above. The fourth row of the figure

 The whole steps are:

  1. First of all, the monitor object is not bound to any object at the beginning. When the synchronized code block is executed for the first time, its object will be bound to the underlying monitor object, and the pointer address of the monitor object will be replaced with the markword of the object itself.
  2. Set the owner of the monitor object to the stack frame of the current thread
  3. When a second thread enters the synchronized code block, first determine whether the markword of the current object points to the monitor. If not, perform the first two steps. Otherwise, determine whether the owner points to a certain thread. If so, enter the entrylist of the monitor. Enter blocking and wait for the owner to complete execution. If not, execute steps 1 and 2.
  4. When releasing the lock, first find the reference address of the current object, then get the monitor object address in the markword, then clear the current thread pointed to by the owner from the monitor, and then get the markword in the normal state of the object from the monitor to reset and restore Normal state, then wake up all threads in the entrylist, seize time and repeat the above steps by slice.
  5. Don’t worry when an exception occurs in the synchronized code block. It will also help us reset the state of the current object and wake up the thread in the entrylist, and then throw an exception to avoid deadlock.

2. Lightweight lock

Lightweight locks are not used to replace heavyweight locks. We know that monitor is an object provided to us by the operating system. Frequent operations to unlock it are actually a very performance-consuming operation. When our thread competition is not so frequent, When, for example, the previous thread executes the code block and the next thread enters, then there is no need for me to lock. When competition is frequent, we will upgrade to a heavyweight lock.

Specific steps are as follows:

  1. First, when the synchronized code block is executed, a lock record will be written in the current thread stack, and then the reference address of the current object will be written into the object reference of the lock record.
  2. Exchange the markword of the current object and the address of the lock record through cas. Switching the status to 00 is a lightweight lock. See the third line of Figure 1. If the status of the current object is 01, the exchange can be successful, indicating that the current object is changed from the current The thread holds it. If it is 00, the exchange will fail, indicating that the previous lock is already held by someone else. At this time, it will be divided into two situations:
  • The first thread re-enters, which means that the current thread holds the lock. Then, because the synchronized code block is executed again, another stack frame lock record is written to the stack on the left, but because the replacement fails, the address of the lock record is null. The number of current thread locks is increased by one. Because the current thread has acquired the lock, it can still enter the code block for execution. When the inner synchronized code block is executed, the number of current thread locks will be reduced by one. When the execution reaches the outermost In the code block of the layer, it is judged that if the lock record has a value, it means it is the last layer, and the number of locks of the current thread is judged. If it is 0, the lock is released;
  •   The second is that if the lock is not acquired by the current thread, then the lock will be upgraded to a weight lock (or technically called lock expansion), and the markword of the object will be replaced from the lock record address to the address of the monitor object, and Point the owner of the monitor to the stack address of the previous thread, and put the current thread into the entrylist to block and wait.

Guess you like

Origin blog.csdn.net/weixin_59244784/article/details/132984166