In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-11 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
In this issue, the editor will bring you about how to use Java to achieve synchronized lock synchronization mechanism. The article is rich in content and analyzes and describes for you from a professional point of view. I hope you can get something after reading this article.
Principle of synchronized implementation
Synchronized implements the locking mechanism by entering and exiting Monitor objects, and the code block is implemented by a pair of monitorenter/monitorexit instructions. After compilation, the monitorenter instruction is inserted at the beginning of the synchronous code block, the monitorexit instruction is inserted at the end of the method and the exception, and the JVM ensures that monitorenter and monitorexit appear in pairs. Any object has a Monitor associated with it, and if and only if a Monitor is held, it will be locked.
When executing monitorenter, first try to acquire the lock of the object. If the object is not locked or the current thread holds the lock, the counter of the lock is increased by 1; accordingly, when the monitorexit instruction is executed, the counter of the lock is subtracted by 1. When the counter is reduced to 0, the lock is released. If the monitorenter fails to acquire the lock, the current thread is blocked until the object lock is released.
Before JDK6, the implementation of Monitor relied on the implementation of mutexes within the operating system (usually implemented by Mutex Lock). Thread blocking would switch between user mode and kernel mode, so the synchronization operation was an undifferentiated heavy lock.
Later, JDK upgrades synchronized to avoid switching threads between user mode and kernel mode when threads are blocked, adding spin operations before the operating system blocks threads. Then three different kinds of Monitor are implemented: bias lock (Biased Locking), lightweight lock (Lightweight Locking), and heavy lock. After JDK6, the performance of synchronized has been greatly improved, compared with ReentrantLock, the performance is not poor, but ReentrantLock is more flexible to use.
Adaptive spin (Adaptive Spinning)
The implementation of blocking has the greatest impact on the performance of synchronized. Both suspended threads and recovery threads need the help of the operating system, which needs to be transferred from user mode to kernel state, and the state transition takes a lot of CPU time.
In most of our applications, the locked state of shared data only lasts for a short period of time, and the time it takes to suspend and reply threads is not worth it. Moreover, most of today's processors are multi-core processors, so if you let the latter thread wait a little longer, do not release the CPU, wait until the previous thread releases the lock, and the latter thread immediately acquires the lock and executes the task. This is the so-called spin, let the thread execute a busy loop, turn in place for a while, every turn to see if the lock is released, release directly acquire the lock, do not release the circle again.
The spin lock was introduced in JDK 1.4.2 (turned on with the-XX:+UseSpinning parameter), and JDK 1.6 is turned on by default. Spin lock can not replace blocking, because spin waiting avoids the overhead of thread switching, but it takes up CPU time. If the lock occupies a short time, the spin waiting effect is good, otherwise, it is a waste of performance. So adaptive spin lock is introduced in JDK 1.6.If the same lock object, the spin wait has just succeeded, and the thread holding the lock is running, then the spin is likely to succeed, allowing the spin wait duration to be longer. On the other hand, if spin is rarely successful for a lock, then the spin process is likely to be omitted directly, avoiding a waste of CPU resources.
Lock upgrade
Java object header
The lock used by synchronized exists in the Java object header, and the data stored in the Mark Word in the object header varies with the flag bit, as follows:
Java object header Mark Word
Bias lock (Biased Locking)
In most cases, the lock is not only not multithreaded, but is always acquired by the same thread many times. In order to make it cheaper for the thread to acquire the lock, a biased lock is introduced.
When a thread accesses the synchronous block and acquires the lock, the thread ID of the lock bias is stored in the lock record in the object header and stack frame. Later, the thread does not need to lock and unlock the synchronized block when entering and exiting the block. It is simply necessary to test whether the biased lock pointing to the current thread is stored in the Mark Word of the object header. Biased locks are introduced to minimize unnecessary lightweight lock execution paths without multithreaded competition, because the acquisition and release of lightweight locks depend on multiple CAS atomic instructions, while biased locks rely on only one CAS atomic instruction when replacing ThreadID (because biased locks must be undone in the event of multithreaded competition) Therefore, the performance loss of lock-biased undo operations must be less than the performance consumption of saved CAS atomic instructions).
Biased lock acquisition
When the lock object is first acquired by the thread, the flag bit of the object header is set to 01 and the bias mode is set to 1, indicating that it enters the bias mode.
Test whether the thread ID points to the current thread, if so, execute the synchronous code block, if not, enter 3
Use the CAS operation to record the thread ID that acquired the lock in the object's Mark Word. If successful, execute the synchronous code block. If it fails, it means that there has been a bias lock held by another thread, and the current thread starts to attempt to acquire the bias lock.
When the global safe point is reached (no bytecode is executing), the thread with the bias lock is paused to check the thread state. If the thread has ended, the object header is set to unlocked (flag bit is "01"), and then re-biased towards the new thread; if the thread is still alive, undo the bias lock and upgrade to the lightweight lock state (flag bit is "00"). At this time, the lightweight lock is held by the thread that originally held the bias lock and continues to execute its synchronization code, while the competing thread spins and waits for the lightweight lock to be acquired.
Biased lock release
The release of biased locks uses an inert release mechanism: biased locks are not released until competition arises. The release process is step 4 mentioned above, and I will not repeat it here.
Turn off the bias lock
Skew lock is not suitable for all application scenarios. Undo operation (revoke) is a heavy behavior, which can be significantly improved only when there are more synchronous blocks that will not really compete. Skew locks have always been controversial in practice, and some people even think that when you need to use a lot of concurrent class libraries, it often means that you don't need skew locks.
So if you determine that the lock in the application is usually competitive, you can turn off the biased lock:-XX:-UseBiasedLocking=false with the JVM parameter, and the program will enter the lightweight lock state by default.
Lightweight lock (Lightweight Locking)
Lightweight lock is not used to replace heavy lock, its original intention is to reduce the performance loss caused by the use of operating system mutex in traditional heavyweight locks without multithread competition.
Lightweight lock acquisition
If the lock state of the synchronization object is unlocked (the lock flag bit is "01", and whether the bias lock is "0"), the virtual machine will first establish a space called lock record (Lock Record) in the stack frame of the current thread, which is used to store a copy of the current Mark Word of the lock object, officially known as Displaced Mark Word. At this point, the state of the thread stack and object header is shown in the following figure:
Copy the Mark Word in the object header to the lock record (Lock Record).
After the copy is successful, the virtual machine will use the CAS operation to attempt to update the Mark Word of the object to a pointer to Lock Record and point the owner pointer in Lock record to object mark word.
If successful, the current thread holds the object lock, sets the Mark Word lock flag bit of the object header to "00", indicating that the object is in a lightweight locked state, and executes the synchronization code block. At this point, the state of the thread stack and object header is shown in the following figure:
If the update fails, check whether the Mark Word of the object header points to the stack frame of the current thread. If so, the current thread owns the lock and executes the synchronized code block directly.
If no, multiple threads compete for the lock, and if there is only one waiting thread, the lock is acquired through a spin attempt. When the spin exceeds a certain number of times, or another thread competes for the lock, the lightweight lock expands to a heavyweight lock. The heavyweight lock blocks all threads except the thread that owns the lock, preventing the CPU from idling, and the state value of the lock flag becomes "10". The pointer to the heavyweight lock (mutex) is stored in Mark Word, and the thread waiting for the lock will also enter the blocking state.
Lightweight lock unlock
The time for a lightweight lock to unlock is when the current thread synchronization block is executed.
Try to replace the current Mark Word with the Displaced Mark Word object copied in the thread through the CAS operation.
If successful, the whole synchronization process is completed
If it fails, there is competition, and the lock expands to a heavy lock. When the lock is released, the suspended thread is awakened.
Weight lock
Lightweight locks adapt to scenarios where threads execute synchronous blocks almost alternately. If the same lock object is accessed at the same time (the first thread holds the lock and the second thread spins more than a certain number of times), the lightweight lock will expand to a heavy lock, and the lock mark bit of Mark Word will be updated to 10 # Mark Word to point to the mutex (heavyweight lock).
The heavyweight lock is realized through a monitor inside the object, which in essence depends on the Mutex Lock (mutex lock) of the underlying operating system. Switching between threads in the operating system requires a transition from user mode to core mentality, which is very expensive, and the transition between states takes a relatively long time, which is why before JDK 1.6, synchronized heavyweight locking efficiency was low.
The following figure shows the conversion object Mark Word data transformation between bias locks, lightweight locks, and heavyweight locks:
Conversion between bias lock, lightweight lock and heavyweight lock
There is a relatively complete lock upgrade process on the Internet:
Lock upgrade process
Lock removal (Lock Elimination)
Lock elimination means that when the virtual machine just-in-time compiler is running, the lock will be deleted if it is detected that there is no shared data competition for some synchronous code. That is, the just-in-time compiler removes unnecessary locking operations as appropriate.
The basis of lock elimination is escape analysis. To put it simply, escape analysis is the dynamic scope of the analysis object. There are three situations:
Do not escape: the scope of the object is only in this thread and this method
Method escape: after the object is defined within the method, it is referenced by the external method
Thread escape: when an object is defined within a method, it is referenced by an external thread
The just-in-time compiler optimizes for different situations of the object:
Allocation on the object stack (not supported by Stack Allocations,HotSpot): create objects directly on the stack.
Scalar substitution (Scalar Replacement): splits objects and directly creates member variables used by the method. Provided that the object does not escape from the scope of the method.
Synchronization cancellation (Synchronization Elimination): lock elimination, provided that the object does not escape the thread.
For lock elimination, that is, in escape analysis, those locked objects that will not escape the thread can be deleted directly.
Look at an example through the code:
Public void elimination1 () {final Object lock = new Object (); synchronized (lock) {System.out.println ("the lock object does not only scope this thread, so the lock is eliminated.") ;} public String elimination2 () {final StringBuffer sb = new StringBuffer (); sb.append ("Hello,"). Append ("World!"); return sb.toString ();} public StringBuffer notElimination () {final StringBuffer sb = new StringBuffer (); sb.append ("Hello,"). Append ("World!"); return sb;}
The lock scope of the lock object in elimination1 () is only within the method, and there is no escape thread, as is the case with sb in elimination2 (), so the synchronization locks for both methods are eliminated. But the sb in the notElimination () method is the return value of the method, which may be modified by other methods or by other threads, so looking at this method alone will not eliminate the lock, but also depends on the calling method.
Lock roughening (Lock Coarsening)
In principle, when writing code, we should limit the scope of the synchronization block to as little as possible. So that the number of operations that need to be synchronized is as small as possible, and when there is lock competition, the waiting thread acquires the lock as soon as possible. But sometimes, if a series of consecutive operations repeatedly lock and unlock the same object, or even lock operations appear in the body of the loop, even if there is no thread competition, frequent mutually exclusive synchronization operations can also lead to unnecessary performance loss. If the virtual machine detects that a series of piecemeal operations are locked on the same object, the scope of locking synchronization will be extended (coarsening) to the outside of the entire operation sequence.
For example, in the elimination2 () method in the above example, the append of StringBuffer is a synchronous method. Lock coarsening occurs when you operate frequently, and the final result is similar (only similar, not true):
Public String elimination2 () {final StringBuilder sb = new StringBuilder (); synchronized (sb) {sb.append ("Hello,") .append ("World!"); return sb.toString ();}}
Or
Public synchronized String elimination3 () {final StringBuilder sb = new StringBuilder (); sb.append ("Hello,"). Append ("World!"); return sb.toString ();} final summary
There are two things that affect performance in synchronization operations:
The locking and unlocking process requires additional operation
The conversion cost between user state and kernel state is relatively high.
Synchronized has a number of optimizations in JDK 1.6: hierarchical locks (bias locks, lightweight locks, heavyweight locks), lock elimination, lock coarsening, and so on.
Synchronized reuses the Mark Word state bits of the object header to achieve different levels of lock implementation.
The above is the editor for you to share how to use Java to achieve synchronized lock synchronization mechanism, if you happen to have similar doubts, you might as well refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 215
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.