In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "how to master the Synchronized keyword". The content in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to master the Synchronized keyword".
I. the usage of synchronized
1.1, three ways of use
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Static method
Non-static method
Code block
Code example:
Public class Test {/ / object Object object=new Object (); / / shared variable private static int num; / / static method public synchronized static void lock1 () {num + +;} / Common method public synchronized void lock2 () {num + +;} public void lock3 () {/ / Code block synchronized (object) {num + + }
1.2, scope of action
During the interview, I often ask: what is locked by the synchronized keyword? Or what is its scope of action?
To sum up:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Non-static methods lock the current object (that is, this)
The static method locks the class object Test.class
The code block locks a custom Object object
1.3, atomicity, visibility, ordering
We all know that concurrent programming needs to consider three issues: atomicity, visibility, and orderliness.
So, how do you solve these three problems by using the synchronized keyword?
Atomicity: the synchronized keyword ensures that only one thread can get the lock and enter the synchronous code block without atomicity problems.
Visibility: when performing synchronized, the corresponding lock atomic operation will clear the value of this variable in the working memory and re-read to refresh the memory without visibility problems.
Orderability: reordering can still occur when synchronized is executed, but we have synchronous code blocks to ensure that only one thread executes the code in the synchronized code and there is no ordering problem.
Second, object memory layout
As mentioned above, all three ways are locked objects, objects, objects (say three times), but it sounds very abstract, objects can still be locked? What should I do?
In fact, it has something to do with the object memory layout.
Hearing is false, seeing is believing. Let's see with your own eyes what the object is made of.
Sample code:
/ / 1. Import package import org.openjdk.jol.info.ClassLayout; / / 2, define Lock class public class Lock {int i; boolean flag;} / / 3, print out Lock objects public class Test {public static void main (String [] args) {Lock lock = new Lock (); System.out.println (ClassLayout.parseInstance (lock). ToPrintable ());}}
The printed result looks like this:
OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 47 70 9d (00000001 01000111 01110000 10011101) (- 1653586175) 4 4 (object header) 110000 00 (00010001 00000000 00000000000000000000000000000000000000) (17) 8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (- 134168253) 12 4 int L.I 0 16 1 boolean L.flag false 17 7 (loss due to the next object alignment) Instance size: 24 Bytes Space losses: 0 bytes internal + 7 bytes external = 7 bytes total
Explain the printed results in detail:
2.1.Objectheader (Object Header)
Object Header is made up of MarkWord and Class Pointer, which will be explained in detail later.
Print result: takes up 4'4'4'12 bytes.
2.2. Instance data (Interface Data)
The object instance data includes all the member variables of the object, and its size is determined by the size of each member variable.
Of course, static member variables are not included because they are maintained in the method area!
Print result: you can see that int L.I and boolean L.flag are the instance data, occupying 4 / 1 / 5 bytes.
2.3.Fill data (Padding)
The space occupied by Java objects is 8 bytes aligned, that is, the number of bytes occupied by all Java objects must be a multiple of 8, because when we take a data from disk, it will not be a byte to read, but a whole block to read, the size of this block is 8 bytes, so in order to be complete, the role of padding is to supplement bytes and ensure that the object is an integer multiple of 8 bytes.
Print result: you can see (loss due to the next object alignment) that this is the fill data, which takes up 7 bytes.
In that case, 12'5'7'24 is a total of 24 bytes, which is a multiple of 8.
Therefore, the memory layout of an object is composed of object header, instance data, and fill data.
Next: focus on this object.
Third, talk about the object head in detail
The above mentioned object head, directly look at the explanation on the official website, the address of the official website at the end of the article:
3.1Objectheader (object header)
Object header:Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.
The common structure at the beginning of each gc-managed heap object. (each oop points to an object header) includes basic information about the layout, type, GC status, synchronization status, and identification hash code of the heap object. It consists of two words. In the array, it is followed by the length field. Note that both Java objects and vm internal objects have a common object header format.
3.2 、 Klass Point
The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable".
The second word of the head of each object. Point to another object (meta-object) that describes the layout and behavior of the original object. For the Java object, "klass" contains a C++-style "virtual function table".
3.3 、 Mark Word
The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.
The first word of the head of each object. It is usually a set of bit fields, including synchronization status and identity hash codes. It may also be a pointer to synchronization-related information (with low-bit coding characteristics). During GC, the GC status bit may be included.
To sum up: in fact, the object header is composed of MarkWord and Klass Point. MarkWord is used to store information such as hashCode, lock information, or generational age or GC logo of an object. Klass Point is an object's pointer to its class metadata, and the virtual machine uses this pointer to determine which class instance the object is.
So here's the problem!
Question: where is the MarkWord stored in hashcode, lock information, or generational age or GC logo defined?
You can download the source code of OpenJDK, and you can see the status information of Mark Word in the markOop.hpp file:
MarkOop.hpp
You can see or write very clearly, draw a picture and sum it up:
Mark Word space
IV. Synchronized in-depth analysis
Compile Test.java to Test.class, and execute the command javap-v Test.class in the corresponding directory, and you can see the corresponding bytecode, as follows:
Bytecode
As you can see in the figure above, JVM handles synchronization methods and code blocks differently.
For synchronization code blocks: monitorenter and monitorexit instructions are used to achieve synchronization.
The monitorenter instruction can be understood as locking, and monitorexit as releasing the lock.
After entering the monitorenter instruction, the thread will hold the Monitor object, and after exiting the monitorenter instruction, the thread will release the Monitor object.
For the method: the ACC_SYNCHRONIZED flag appears.
When the ACC_SYNCHRONIZED identifier appears, Jvm implicitly calls monitorenter and monitorexit. Monitorenter is called before the synchronization method is executed, and monitorexit is called after the synchronization method is executed to release the Monitor object.
You can find that both synchronized code blocks and synchronous methods have something to do with the Monitor object.
Then there's the problem again!
Question: what is this Monitor object? what are monitorenter and monitorexit?
4.1 、 monitorenter
Look directly at the description of it in the JVM specification. The address is at the end of the article:
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.
Each object is associated with a monitor Monitor. The monitor is locked when it is occupied, and other threads cannot get the Monitor. When JVM executes an onitorenter within a method of a thread, it attempts to take ownership of the Monitor corresponding to the current object.
The execution process is as follows:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
If the entry number of Monior is 0, the thread can enter Monitor and set the entry number of monitor to 1. The current thread becomes the owner owner of the Monitor.
If the thread already has ownership of the Monitor and allows it to reenter the Monitor, the number of entries into the Monitor is increased by 1.
If another thread already takes ownership of the Monitor, the thread currently trying to take ownership of the Monitor will be blocked until the number of entries to the Monitor becomes zero.
4.2 、 monitorexit
Look at the description of it in the JVM specification. The address is at the end of the article:
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
The execution process is as follows:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
The thread that can execute the monitorexit instruction must be the thread that owns the Monitor of the current object.
When monitorexit is executed, the number of inbound Monitor is reduced by 1. When the number of Monitor entries is reduced to 0, the current thread exits the Monitor and no longer has ownership of the Monitor, and other threads blocked by the Monitor can try to take ownership of the Monitor.
4.3.The Monitor monitor
Each object is associated with a Monitor object, also known as a monitor.
In the HotSpot virtual machine, Monitor is implemented by ObjectMonitor. The source code is implemented by C++ and is located in the source ObjectMonitor.hpp file of the HotSpot virtual machine (path: src/share/vm/runtime/objectMonitor.hpp)
The main data structures of ObjectMonitor are as follows:
ObjectMonitor () {_ header = NULL; _ count = 0; _ waiters = 0, _ recursions = 0; / / number of reentrants of the thread _ object = NULL; / / Store the monitor object _ owner = NULL; / / identifies the thread that owns the monitor _ WaitSet = NULL / / threads in wait state will be added to _ WaitSet _ WaitSetLock = 0; _ Responsible = NULL; _ succ = NULL; _ cxq = NULL; / / one-way list of multiple threads competing for locks FreeNext = NULL; _ EntryList = NULL; / / threads waiting for locks will be put here _ SpinFreq = 0; _ SpinClock = 0; OwnerIsThread = 0 }
Seeing here, I believe you can understand why you have to explain the object memory layout and object header before, because there is a corresponding relationship among the three.
Draw a picture to sum up:
You can see that the data structure of ObjectMonitor contains: _ owner, _ WaitSet, and _ EntryList.
The transformation of the relationship between them is as follows:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
When multiple threads access the same block of code or a synchronization method at the same time, these threads are first placed in the _ EntryList queue, and threads in the blocked state are placed in that queue.
When a thread gets the Monitor of an object, it can enter the running state and execute the code logic. At this point, the _ owner of the ObjectMonitor object points to the current thread, and _ count plus 1 means that the current object lock is acquired by a thread. The thread that did not acquire the lock will enter _ EntryList again and be suspended.
When a thread in running state calls the wait () method, the current thread releases the Monitor object and enters the waiting state, the _ owner of the ObjectMonitor object becomes null,_count minus 1, and the thread enters the _ WaitSet queue until a thread calls the notify () method to wake up the thread, the thread enters the _ EntryList queue again until it competes to the lock again and then enters the _ owner area.
If the current thread finishes executing, the monitor object is also released, and the _ owner of the ObjectMonitor object becomes null,_count minus 1.
This process is roughly the principle that was implemented before JDK6.
However, before JDK6, the efficiency of the synchronized keyword was very low.
The reasons are as follows:
The Monitor object relies on the Mutex Lock of the underlying operating system to achieve mutual exclusion. If the thread applies for the Mutex successfully, it holds the Mutex, and other threads will not be able to obtain the Mutex.
Since Mutex Lock involves the underlying operating system, there is a transition between operating system user mode and kernel state at this time, which consumes a lot of system resources, because both user mode and kernel state have their own dedicated memory space, special registers and so on. Switching from user mode to kernel state requires passing many variables and parameters to the kernel. The kernel also needs to protect some register values, variables and so on when switching in user mode.
Therefore, after JDK 6, it is optimized from the Jvm level, which is divided into bias lock, lightweight lock, spin lock and weight lock.
Lock upgrade
Here's how the lock is upgraded step by step.
5.1, bias lock
1. What is a bias lock?
Through research and practice, the author of HotSpot found that in most cases, the lock not only does not exist multi-thread competition, but is always acquired by the same thread many times. In order to make the thread acquire the lock at a lower cost, a biased lock is introduced.
Biased lock "bias" means that the lock will be biased towards the first thread to acquire it, and will store the lock-biased thread ID in the object header. Later, when the thread enters and exits the synchronization block, it only needs to check whether it is biased lock, lock flag bit, and ThreadID.
Bias lock Mark Word
However, the biased lock must be undone in the event of multiple thread contention, so the performance consumption of undoing the biased lock must be less than the performance consumption of the previously saved CAS atomic operation, otherwise the loss will outweigh the gain.
2. the principle of bias lock
Flowchart of conversion from unlocked to biased locks:
Bias lock flow chart
Parameter:-XX:+UseBiasedLocking turns on bias lock
To put it simply:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
The thread accesses the synchronous code block and uses the CAS operation to put the Thread ID into the MarkWord
If the thread CAS succeeds, the thread will acquire the bias lock at this time
If the thread CAS fails, proving that another thread already holds the lock, start biased lock revocation at this time, and do the following
3. Revocation of bias lock
The process is as follows:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Undo actions that favor locks must wait for the global security point.
Pause the thread that originally held the biased lock
Set Thread ID to null to make it unlocked
Restore the original holding bias lock thread and start the lightweight locking process
5.2 lightweight lock
1. What is a lightweight lock?
Lightweight lock is a lock mechanism added in JDK 6, and the "lightweight" in its name is relative to the traditional lock that uses monitor, so the traditional locking mechanism is called "heavyweight" lock. It should be emphasized that lightweight locks are not used instead of heavyweight locks.
The purpose of introducing lightweight locks is to avoid the performance consumption caused by heavy locks when multiple threads execute synchronous blocks alternately, but if multiple threads enter the critical area at the same time, it will cause lightweight locks to expand and upgrade heavy locks, so the emergence of lightweight locks is not to replace heavy locks.
2. The principle of lightweight lock
When turning off the bias lock function or when multiple threads compete for a bias lock that causes the bias lock to escalate to a lightweight lock, an attempt is made to acquire a lightweight lock.
The flow chart is as follows:
Lightweight lock upgrade process
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Determine whether the current object is in an unlocked state (hashcode, 0,01). If so, JVM will first establish a space called lock record (Lock Record) in the stack frame of the current thread to store a copy of the current Mark Word of the lock object (officially prefixed with a Displaced, that is, Displaced Mark Word), copy the Mark Word of the object to the Lock Record in the stack frame, and point the owner in the Lock Reocrd to the current object.
JVM uses the CAS operation to attempt to update the Mark Word of the object to a pointer to Lock Record, which, if successful, indicates a contention to the lock, then changes the lock flag bit to 00 and performs a synchronization operation.
If it fails, it determines whether the Mark Word of the current object points to the stack frame of the current thread, and if so, it means that the current thread already holds the lock of the current object, then the synchronous code block is executed directly; otherwise, it only means that the lock object has been preempted by other threads, then the lightweight lock needs to be expanded into a heavy lock, the lock flag bit is changed to 10, and the thread waiting behind will enter the blocking state.
5.3 spin lock
1. Why is there a spin lock?
When talking about monitor locking, we know that monitor will block and wake up threads, and thread blocking and awakening requires CPU to change from user mode to core mentality. Frequent blocking and awakening is a heavy task for CPU, which brings great pressure on the concurrency performance of the system.
At the same time, the virtual machine development team also noticed that in many applications, the locked state of shared data only lasts for a short period of time, and it is not worth blocking and waking up threads for this period of time.
If the physical machine has more than one processor that allows two or more threads to execute in parallel at the same time, we can ask the later thread that requests the lock to "wait a minute" without giving up the processor's execution time. see if the thread holding the lock will release the lock soon. In order for the thread to wait, we simply have the thread perform a loop (spin), which is called a spin lock.
2. Advantages and disadvantages of spin lock.
Spin waiting is not a substitute for blocking, not to mention the requirement for the number of processors, spin waiting itself avoids the overhead of thread switching, but it takes up processor time.
If the lock is occupied for a short time, the spin waiting effect will be very good, on the contrary, if the lock is occupied for a long time. Then spinning threads will only consume processor resources in vain, and will not do any useful work, but will lead to a waste of performance.
Therefore, the spin waiting time must be limited, and the performance consumption caused by heavy locking can be avoided if multi-threads execute synchronous blocks alternately.
If you spin more than the limited number of times and still fail to acquire the lock, you should suspend the thread in the traditional way. The default number of spins is 10, which you can change using the parameter-XX: PreBlockSpin.
5.4 Adaptive spin lock
An adaptive spin lock is introduced in JDK 6. Adaptation means that the spin time is no longer fixed, but is determined by the last optional time on the same lock and the state of the lock's owner.
If, on the same object lock, spin wait has just successfully acquired the lock, and the thread holding the lock is running, the virtual machine will think that the spin is likely to succeed again, which in turn will allow the spin wait to last relatively longer, such as 100 cycles.
If spin is rarely successfully acquired for a lock, the spin process may be omitted when acquiring the lock in the future, avoiding a waste of server processing resources.
With adaptive spin lock, the virtual machine's prediction of the status of the program will become accurate, and the performance will be improved.
Thank you for your reading, the above is the content of "how to master the Synchronized keyword", after the study of this article, I believe you have a deeper understanding of how to master the Synchronized keyword, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
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: 0
*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.