Synchronized is an implicit lock in Java. Its acquisition lock and release lock are both It is implicit and completely left to the JVM to help us operate it. Before understanding the Synchronized keyword, the first knowledge point to learn is the Java object structure, because the Synchronized lock is stored in the Java object. The Java object structure is as shown in the figure below. :
You can clearly see that a Java object consists of three parts, namely object header, instance data, and fill data. Our lock is stored in the object header, and then Next we will do a simple analysis of the object structure:
mark-down: The object mark field occupies 8 bytes and is used to store information about the lock's mark bits. From the figure It can be seen that there are hash values, lightweight lock flags, bias lock flags, etc.
Klass Pointer: The type pointer of the Class object. It is the pointer to which Class the current object belongs to. By default, jdk1.8 turns on the compressed pointer and occupies 4 bytes. Turn off the compressed pointer. It occupies 8 bytes.
Actual data of the object: 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.
Fill it in: This part is just for space completion and serves as a placeholder, because the memory management system of the HotSpot virtual machine requires that the starting address of the object must be It is an integer multiple of 8 bytes, so if the object instance is not aligned, it needs to be padded.
In the mark-down lock type mark, you can see that there are a total of five types, namely no lock, biased lock, lightweight lock, heavyweight lock, and GC mark. Therefore, it cannot be fully expressed if only a 2-bit mark is used, so a bias lock mark is introduced, that is to say, 001 means no lock and 101 means bias lock.
The object structure is introduced above. You can see that different lock information will be stored in Mark-down. When the lock status is heavyweight lock (10) , Mark-down will store a pointer to the Monitor object. This Monitor object is also called the monitor lock.
The operating mechanism of synchronized is that when the JVM detects different competition situations in shared objects, it will automatically switch to a suitable lock implementation. This switching is the upgrade or downgrade of the lock. (Many places say that locks can only be upgraded, not downgraded. In fact, this statement is wrong. In the book "The Art of Java Concurrent Programming", it is said that for biased locks, it can be downgraded to a lock-free state, and It's called biased lock revocation).
There are currently three different Monitor implementations, namely biased locks, lightweight locks and heavyweight locks. When a thread holds a Monitor, it acquires the lock.
Monitor in Java is implemented based on C's ObjectMonitor. Its main members include:
_owner: points to the thread holding the ObjectMonitor object
_WaitSet: stores the thread queue in the wait state, that is, the thread that calls the wait() method
_EntryList: stores the thread queue in the waiting lock Block state
_count: Approximately the sum of the number of nodes in _WaitSet _EntryList
_cxq: Multiple threads competing for the lock will first store this one-way Linked list
_recursions: Record the number of reentries
_object: Stored Monitor object
Get When the thread of the Monitor object enters the _owner area, _count is 1. If the thread calls the wait() method, the Monitor object will be released (the lock is released), and _owner will be restored to empty and _count-1. At this time, the thread enters the _WaitSet queue, waiting to be awakened.
As can be seen from the above description, the key to obtaining the lock with the synchronized keyword lies in the object header of each object. This also explains why any object stored in synchronized() brackets can obtain the lock. feature.
Atomicity means that an operation is either completed or not completed. There is no such thing as half-completed, which means that the operation is not possible. Interrupted.
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 don’t understand this, then imagine the following scene. There is a toilet with only one pit, and The toilet is also locked to prevent the uncivilized phenomenon of multiple people going to the toilet at the same time. Everyone who uses the toilet must go to the toilet administrator to pay. After paying, you can get the lock before going to the toilet. After using the toilet, you can Return it to the toilet manager, synchronized is the toilet manager, 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++; } }
Decompile it and view the code:
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 it has the synchronization flag ACC_SYNCHRONIZED, the thread that enters this method first will have the owner of the Monitor first. At this time, the counter is 1
monitorexit: When executing After exiting, the counter -1, returns to 0 and is acquired by other entering threads
Visibility refers to when multiple threads access the same variable When a thread modifies the value of this variable, other threads can immediately sense it and see the modified value. Thread visibility is closely related to JMM. In the next article, we will explore how to use the volatile keyword to achieve visibility
And Synchronized has visibility because it has the following semantics for locking and releasing locks:
Before the thread locks, the value of the shared variable in the working memory must be cleared to read the latest value of the shared variable from the main memory.
When the thread releases the lock, the value of the shared variable must be refreshed into the main memory.
The visibility of synchronized depends on the operating system kernel mutex implementation, which is equivalent to lock and unlock in the JVM. When exiting the code block, the shared variables need to be refreshed to the main memory. This point Unlike the volatile keyword, the visibility of the volatile keyword relies on memory barriers (also called memory barriers).
as-if-serial is to ensure that no matter how the compiler and processor reorder instructions for performance optimization, it needs to be guaranteed The correctness of the running results under single thread. That is to say: If you observe within this thread, all operations are in order, If you observe another thread in one thread, all operations are out of order.
Note that the ordering here is different from volatile. It is not volatile to prevent instruction reordering.
The concept of 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 Releasing the lock also requires releasing the same number of locks. In the synchronized lock object, there is a counter used to record the number of times the lock is acquired, that is, the number of reentrants.
synchronized locks have four alternate upgrade states: no lock, biased lock, lightweight lock and heavyweight. These states gradually escalate with competition. , A complete lock upgrade diagram will be added later .
The above is the detailed content of What is the principle of Synchronized in Java. For more information, please follow other related articles on the PHP Chinese website!