General documentation: Article directory Github: github.com/black-ant
1.1 synchronized briefly
Synchronized is a heavyweight lock that ensures that only one thread can execute a method or block of code at a time.
-
The main object of action is shared data that exists in methods or code blocks, while ensuring that changes in one thread (mainly changes in shared data) are seen by other threads
-
Synchronized’s core principles are Java object headers and Monitor
1.2 Java object Headers and Monitor
/ / principle
1Java object head and Monitor head | - > object: Mark Word (tag field), Klass Pointer type (Pointer) | - > Klass Pointer: a meta data Pointer, decide what data | - > Mark Word: Its runtime data (hashcode, lock state, bias, flags, etc.) | - > Monitor: | - > mutually exclusive: a Monitor lock can only be occupied by one thread at a time// Mark Word and Class Metadata Address structure-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- virtual flight reservation number of head of object structure |---------|-----------------------|---------------------------------------------------------------|32/64bit Mark Word stores information such as an object's hashCode, lock information, or generational age or GC flag32/64bit Class Metadata Address type pointer to the Class Metadata of an object. The JVM uses this pointer to determine which Class the object is an instance of. --------------------------------------------------------------------------------------------------// Mark Word default storage structure for 32-bit JVMS-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 25 bit 4 bit lock state 1 bit is a bit biased locking 2 lock flag bit | -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | Unlocked state object HashCode object generational age0 01
--------------------------------------------------------------------------------------------------
Copy the code
@ https://blog.csdn.net/javazejian/article/details/70768369 / / Monitor the implementation of the wayObjectMonitor has two queues and an area _WaitSet and _EntryList, Use to hold a list of ObjectWaiter objects (every thread waiting for a lock is wrapped into an ObjectWaiter object) _owner (pointing to the thread holding the ObjectMonitor object) areaCopy the code
- 1 When multiple threads access a synchronized piece of code at the same time, they first enter the _EntryList collection and attempt to obtain the Monitor
- 2 When the thread gets the monitor of the object, it enters the _Owner area, and sets the owner variable in monitor to the current thread and increses the count in monitor by 1
- 3 If a thread calls wait(), the current monitor is released, the owner variable is restored to NULL, the count is decrement by 1, and the thread enters the WaitSet set waiting to be awakened.
- 4 If the current thread finishes executing, it will release the monitor(lock) and reset the value of the variable, so that other threads can enter to acquire the monitor(lock).
/ / synchronized @ https://blog.csdn.net/javazejian/article/details/70768369 source code analysis
// The specific process can refer to the specific analysis of the above blog, here is only a summary, big guys liver good
// Synchronized code block underlying logic (monitorenter and MonitoreXit)
> synchronizedThe implementation of the synchronized block uses the monitorenter and monitorexit directives, with the monitorenter indicating the Start of the synchronized block and the monitorexit indicating the end of the synchronized block Start: When monitorenter is executed, the current Thread will attempt to acquire the ownership Thread- of the monitor to which the objectref(that is, the object lock) corresponds1 : objectref.monitor = 0--> Get monitor --> set the counter value to1
Thread-2Objectref.monitor = is found0--> block wait --> Thread-1Execute monitorexit and the counter returns0 --> Thread-2Note: The compiler will ensure that no matter how the method is completed, each monitorenter instruction called in the method is executed with its corresponding Monitorexit instruction, and the exception handler will handle the exception when the method is abnormalCopy the code
Low-level logic of synchronized methods (ACC_SYNCHRONIZED identifier)
-
Method level synchronization is implicit, that is, not controlled by bytecode instructions, and is implemented in method calls and return operations.
-
The JVM can tell whether a method is synchronized from the ACC_SYNCHRONIZED access flag in the method_info Structure in the method constant pool.
- Method calls, call instruction will check to see if method ACC_SYNCHRONIZED access mark is set | – if set, the execution thread will hold the monitor (virtual machine specification is used in the tube side) the word, and then execute method, Finally, the monitor is released when the method completes, either normally or abnormally.
- During method execution, the executing thread holds the monitor, and no other thread can get the same monitor again.
Error: If an exception is thrown during the execution of a synchronized method and the exception cannot be handled inside the method, the monitor held by the synchronized method is automatically released when the exception is thrown outside the synchronized method
Synchronized memory level principle
// The final generated assembly language
lock cmpxchg %r15, 0x16Lock CMPXCHG %r10, (%r11)Copy the code
- The underlying meaning of synchronized is to set the “locked” state of the object header by means of lock CMPXCHG
- To release the lock, change the lock flag bit of the object header to “release” in the lock CMPXCHG mode, and write operations are immediately written back to main memory.
- The JVM further blocks those threads whose CAS failed at synchronized, and this part of the logic is not reflected in the Lock CMPXCHG instruction, which I assume is implemented through some semaphore.
- The lock CMPXCHG instruction guarantees visibility and reordering while the lock CMPXCHG instruction guarantees atomicity of operations.
1.3 use synchronized
// Lock mode, current instance, current class, custom object
> synchronized(this)
> synchronized(object)
> synchronized(class) or static code blocksCopy the code
Three main uses of the synchronized keyword:
-
Modifies an instance method that locks the current object instance and acquires the lock before entering the synchronization code
-
Pertaining to a static method that locks an object of the current class and acquires the lock before entering the synchronization code.
- A static member is not a member of any instance of the class. It is a member of the class. (Static means that this is a static resource of the class.
- So if thread A calls A non-static synchronized method of an instance object, and thread B needs to call A static synchronized method of the class to which the instance object belongs, that’s allowed, no mutual exclusion will occur. This is because access to a lock held by a static synchronized method is a lock of the current class, whereas access to a lock held by a non-static synchronized method is a lock of the current instance object.
-
Modifies a block of code that specifies the object to be locked, locks the given object, and acquires the lock on the given object before entering the synchronized code base.
- Like synchronized methods, a synchronized(this) block locks the current object.
- The synchronized keyword added to both static methods and synchronized(class) code blocks locks the class.
The synchronized keyword is added to a nonstatic static method to lock the object instance. Synchronized (String a) : synchronized(String a) : synchronized(String a)
1.4 Synchronized other knowledge points
/ / to explain
1 synchronizedProvides an exclusive locking mode, which is implemented by the JVM to add and release locks/ / -- > block
当 synchronizedWhen attempting to acquire a lock, the lock cannot be acquired and will remain blockedsynchronizedAnd ReenTrantLock - both are reentrant locks -synchronizedJVM dependent and ReenTrantLock dependent API - ReenTrantLock ratiosynchronizedSome advanced features added? - Wait can be interrupted; Can realize fair lock; Optional notification (locks can bind multiple conditions)Copy the code
Synchronized /notifyAll and wait
The wait-and-wake mechanism needs to be in a synchronized code block or method, and must get the current object’s monitor object before calling these methods
Synchronized and thread interrupt
A thread’s interrupt operation has no effect on synchronized methods or blocks of code waiting to acquire the lock object
1.5 Locking concepts in multithreading
1.5.1 Lock classification by Level
Locks can be upgraded according to the following levels: biased lock -> lightweight lock -> heavyweight lock, lock upgrade is one-way
- Biased locking
- Reduces the cost of lock acquisition by the same thread (in most cases, locks are not only non-multi-threaded but are always acquired by the same thread multiple times)
- If a thread acquies the lock, the lock goes into biased mode and the Mark Word structure becomes biased. When the thread requests the lock again, it does not need to do any synchronization
- When a biased lock fails, it does not immediately swell to a heavyweight lock, but is upgraded to a lightweight lock first
- Lightweight lock
- For the vast majority of locks, there is no competition for the entire synchronization period
- Lightweight locking is suitable for situations where threads execute synchronized blocks alternately
- If there are occasions when the same lock is accessed at the same time, this can cause lightweight locks to swell to heavyweight locks
- spinlocks
- Spin-locking assumes that the current thread will acquire the lock in the near future, so the virtual machine makes the current thread that wants to acquire the lock do several empty loops
- After several cycles, if the lock is obtained, the critical region is successfully entered. If the lock is not acquired, the thread is suspended at the operating system level
- Reduced thread context switching, but increased CPU consumption
- Heavyweight lock
1.5.2 Lock Operations
- Remove lock:
- (when the Java virtual machine in the JIT compiler can be simple to understand for the first time a piece of code will be executed when compile, also known as instant compiled), run through the context of the scan, remove lock there can be no Shared resource competition, in this way to eliminate unnecessary lock, can save a meaningless request lock time
// Java has three main locks
synchronizedKeyword ReentrantLock ReentrantLock ReadWriteLock read/write lock// The lock type• Spin lock - Adaptation to spin lock • Lock removal • Lock coarsening • Lock upgrade - Heavyweight lock - Lightweight lock - biased lockCopy the code
1.5.3 Other Lock Concepts
-
Internal lock:
- Synchronized: a reference to a lock object, a block of code protected by the lock
- Each Java object can implicitly play the role of a lock for synchronization, and these built-in locks are called internal or monitor locks.
-
Fair lock/non-fair lock
- A fair lock means that multiple threads acquire the lock in the order in which they apply for the lock. A non-fair lock means that multiple threads acquire the lock in a different order than the order in which they apply for the lock. This may cause priority inversion or starvation.
- The advantage of a non-fair lock is that the throughput is greater than that of a fair lock. ReentrantLock is a default non-fair lock and can be selected by the constructor. Synchronized is a non-fair lock.
- Reentrant lock
- A ReentrantLock is one in which a thread acquies a lock in an outer method and automatically acquies the lock in an inner method. Both ReentrantLock and Synchronized are reentrant.
- Exclusive/shared locks
- A shared lock is a lock that can be shared by multiple threads. For ReadWriteLock, read locks are shared and write locks are exclusive.
- Mutex/read/write lock
- Exclusive/shared lock is a broad term and mutex/read-write lock is a concrete implementation.
- Optimistic lock/pessimistic lock
- The optimistic and pessimistic locks view synchronization differently. The optimistic lock considers that there is no competition for the modification of the same data and attempts to update the data. If the modification fails, the data will be updated again and again.
- Pessimistic locks, in contrast, acquire the lock directly, then operate on it, and finally release it.
- Segmented lock
- Segment locking is a design idea that increases concurrency by dividing an entire body into smaller chunks and placing locks on each of the chunks.
1.6 Lock conversion process
The change in the object header can be seen in the following image, which makes it very clear @https://www.cnblogs.com/jhxxb/p/10983788.html
// As previously known, the state change of the lock is one-way, from biased lock -> lightweight lock -> heavyweight lock
// Bias lock -> bias lock
1An object is biased to a thread. After the synchronization code is executed, when the object enters the safe point, the epoch value in the class object is increased if it needs to be biased again2After exiting the safe point, when a thread attempts to obtain the epoch lock, it directly checks whether the epoch value stored in the class instance object is equal to that stored in the class object. If the epoch value is not equal, it indicates that the epoch lock of the object is invalid. In this case, it can try to bias the object again.// Biased lock -> lightweight lock
1When the object is found to be locked and ThreadID is not itself, it turns to biased locking and creates a Lock Record space in the stack frame of that threadCopy the code
1.7 Why is a lock converted
// Each type of lock has its own characteristics> Biased locking - Advantages: no CAS, low consumption, high performance, reentrant - Disadvantages: High lock undo consumption while lock contention - Scenario: Synchronous code executed by the same thread > Lightweight locking - Advantages: Competing threads do not block - Disadvantages: Lightweight locks use spin when they are not acquired, consuming resources - Scenario: Threads alternately execute a synchronized block or method, seeking response time, and the lock takes very short time > Heavy locks - Advantages: Threads competing do not use spin, only wake up and wait - Disadvantages: The thread blocks and the lock changes consume resources - scenario: The lock is held for a long time in pursuit of throughputCopy the code
1.8 Synchoized source
synchronizedIs a modifier, we need to look at Step from the perspective of C1Download OpenJDK code HTTPS://blog.csdn.net/leisure_life/article/details/108367675
Step 2: Indexes the.c file by code// TODO
Copy the code
1.9 Synchoized usage
public void operation(Integer check) {
// Example 1: Verify no lock
// functionShow(check);
// Example 2: synchronized method verification
functionShowSynchronized(check);
// Example 3: Synchronized code block verification
// statementShowSynchronized(check);
// Example 4: Verify that the code block is an object of Class
// classShowSynchronized(check);
// Example 5: Synchronizing code block Object
// objectShowSynchronized(check);
// Example 5: Synchronizing code block Object
// objectStringShowSynchronized(check);
}
/ * * * * /
public void functionShow(Integer check) {
logger.info("------> check is {} <-------", check);
if (check == 0) {
showNum = 100;
try {
Thread.sleep(2000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
logger.info("------> check is Over {} :{}", check, showNum);
}
/** * Synchronization method */
synchronized public void functionShowSynchronized(Integer check) {
logger.info("------> check is {} <-------", check);
if (check == 0) {
showNum = 100;
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
logger.info("------> check is Over synchronized {} :{}", check, showNum);
}
/ * * * * /
public void statementShowSynchronized(Integer check) {
logger.info("------> check is {} <-------", check);
synchronized (this) {
if (check == 0) {
showNum = 100;
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
}
logger.info("------> check is Over synchronized {} :{}", check, showNum);
}
public void classShowSynchronized(Integer check) {
logger.info("check is {} <-------", check);
synchronized (CommonTO.class) {
if (check == 0) {
showNum = 100;
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
}
logger.info("check is Over synchronized {} :{}", check, showNum);
}
public void objectShowSynchronized(Integer check) {
logger.info("check is {} <-------", check);
synchronized (lock) {
if (check == 0) {
showNum = 100;
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
}
logger.info("check is Over synchronized {} :{}", check, showNum);
}
public void objectStringShowSynchronized(Integer check) {
logger.info("check is {} <-------", check);
synchronized (lock2) {
if (check == 0) {
showNum = 100;
try {
Thread.sleep(5000);
} catch(InterruptedException e) { e.printStackTrace(); }}else if (check == 1) {
showNum = 200;
}
}
logger.info("check is Over synchronized {} :{}", check, showNum);
}
Copy the code