[java] Thorough analysis of Synchronized

foreword

Source level analysis Synchronized

object structure

Synchronized is an implicit lock in Java. Its acquisition and release of locks are implicit, and it is completely left to the JVM to help us operate. Before understanding the Synchronized keyword, the first knowledge point to learn is the object structure of Java, because Synchronized locks are stored in Java objects, and the structure of Java objects is shown in the following figure:
insert image description here

It can be clearly seen that a Java object consists of three parts, namely the object header, instance data, and filling data. Our lock is stored in the object header. Next, we will do a simple analysis of the object structure:

  1. mark-down: The object mark field occupies 8 bytes and is used to store information such as the mark bits of the lock. From the figure, it can be seen that there are hash values, light-weight lock mark bits, and biased lock mark bits.
  2. Klass Pointer: The type pointer of the Class object, which is the pointer to which Class the current object belongs to. jdk1.8 occupies 4 bytes after the compressed pointer is enabled by default, and 8 bytes after the compressed pointer is disabled.
  3. Object actual data: This part includes all member variables of the object, the size is determined by each member variable, for example, byte occupies 1 byte, int occupies 4 bytes, etc.
  4. Fill it: This part of the content is just to complete the space, which is a placeholder, because the memory management system of the HotSpot virtual machine requires that the starting address of the object must be an integer multiple of 8 bytes, so if an object appears If the instance is not aligned, it needs to be supplemented by padding it.

In the mark-down lock type tag, you can see that there are a total of five types, namely lock-free, biased lock, lightweight lock, heavyweight lock, and GC tag, so it cannot be fully represented if only 2-bit tags are used It came out, so a biased lock flag was introduced, that is to say, 001 is no lock and 101 is biased lock.

Monitor object

The object structure is introduced above. You can see that different lock information will be stored in Mark-down. When the lock status is a heavyweight lock (10), a pointer to a Monitor object will be stored in Mark-down. This Monitor object Also known as monitor lock.

The operating mechanism of synchronized is that when the JVM detects that there are different competition conditions in the shared object, it will automatically switch to the appropriate lock implementation. This switch is the upgrade and downgrade of the lock. (Many places say that locks can only be upgraded and cannot be downgraded. In fact, this statement is wrong. It is mentioned in the book "The Art of Java Concurrent Programming" that for biased locks, it can be downgraded to a lock-free state. Called the revocation of biased locks).

So now there are three different Monitor implementations, namely biased locks, lightweight locks, and heavyweight locks. If a Monitor is held by a thread, it means that the thread has acquired the lock.

The Monitor in Java is implemented based on the ObjectMonitor of C++, and its main members include:

  1. _owner: Points to the thread holding the ObjectMonitor object
  2. _WaitSet: Stores the thread queue in the wait state, that is, the thread that calls the wait() method
  3. _EntryList: store the thread queue in the state of waiting for the lock block
  4. _count: about the sum of the number of nodes in _WaitSet+_EntryList
  5. _cxq: multiple threads competing for locks will be stored in this one-way linked list first
  6. _recursions: record the number of reentries
  7. _object: Stored Monitor object

When the thread that acquires the Monitor object enters the _owner area, _count+1, if the thread calls the wait() method, then the Monitor object will be released (release the lock), _owner will return to empty and _count-1. At this point, the thread enters the _WaitSet queue, waiting to be woken up.

As can be seen from the above description, the key to acquire locks with the synchronized keyword lies in the object header of each object, which also explains why any object can acquire locks in the synchronized () brackets.

Synchronized features

atomicity

Atomicity means that an operation is either completed or not completed, and there is no half-completed situation, which means that this operation is uninterruptible.

Synchronized can ensure that only one thread gets the lock at the same time and enters the code block to execute the code. If you can’t understand this, then imagine the following scenario. There is only one pit in the toilet, and the toilet is locked. It is to prevent the uncivilized phenomenon that many people go to the toilet together. Everyone must go to the toilet administrator to pay the fee. After paying the fee, they can get the lock and then go to the toilet. Synchronized is the toilet administrator, ensuring that only one person can get the lock at a time, and everyone must return the key after using the toilet.

Next, see the following synchronous addition method:

public static void add() {
    synchronized (Demo.class) {
        counter++;
    }
}

Check the code after decompiling it:

javap -v -p Demo
public static void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC , ACC_SYNCHRONIZED
    Code:
      stack=2, locals=2, args_size=0
         0: ldc           #12                 // class
         2: dup
         3: astore_0
         4: monitorenter
         5: getstatic     #10                 // Field counter:I
         8: iconst_1
         9: iadd
        10: putstatic     #10                 // Field counter:I
        13: aload_0
        14: monitorexit
        15: goto          23
        18: astore_1
        19: aload_0
        20: monitorexit
        21: aload_1
        22: athrow
        23: return
      Exception table:

You can see that there are two instructions that are obviously related to monitor:

monitorenter: After judging that the thread that has the synchronization flag ACC_SYNCHRONIZED preemptively enters this method, it will first have the owner of the Monitor. At this time, the counter +1 monitorexit
: when the execution is completed and exits, the counter is -1. After returning to 0, it is obtained by other entering threads

visibility

Visibility means that when multiple threads access the same variable, and one thread modifies the value of the variable, other threads can immediately perceive and see the modified value.

Synchronized has visibility because it has the following semantics for locking and releasing locks:

  1. Before the thread is locked, the value of the shared variable in the working memory must be cleared, so as to read the latest value of the shared variable from the main memory.
  2. When the thread releases the lock, the value of the shared variable must be flushed to main memory.
  3. The visibility of synchronized depends on the implementation of the operating system kernel mutex, which is equivalent to the lock and unlock in the JVM. When exiting the code block, the shared variable needs to be refreshed to the main memory. This is different from the volatile keyword, which is visible Sexuality is achieved by relying on memory barriers (also called memory barriers).

orderliness

as-if-serial is to ensure that no matter how the compiler and processor reorder instructions for performance optimization, it is necessary to ensure the correctness of the running results under a single thread. That is to say: if you observe in this thread, all operations are in order, if you observe another thread in one thread, all operations are out of order.
Note that the order and volatile here are not the same, it is not volatile to prevent instruction reordering.

reentrant lock

The concept of a reentrant lock is very simple, that is, a thread can acquire the object lock it holds multiple times. This kind of lock is a reentrant lock. The same release of the lock requires the release of the same number of locks. There is a counter in the synchronized lock object, which is used to store the number of times the lock is acquired, that is, the number of reentries.

The process of lock escalation

The synchronized lock has four alternate upgrade states: no lock, biased lock, lightweight lock, and heavyweight. These states gradually upgrade with competition.

Supongo que te gusta

Origin blog.csdn.net/u011397981/article/details/130460802
Recomendado
Clasificación