One: Java multi-threaded mutex, and Java multithreading introduce the reason for the bias lock and lightweight lock?
--->synchronized of the weight of the lock, that is, when the thread runs to the code block, the program's running level from the user state to the kernel state, to suspend all the threads, so that the CPU through the operating system instructions, to dispatch multithreading between, who executes the code block, Who gets into the blocking state. This will frequently occur in the program running state of the switch, thread suspend and wake up, so that will be a lot of resource consumption, the program is inefficient operation. In order to improve the efficiency, the JVM developers, introduced the biased lock, and the lightweight lock, as far as possible to make multi-threaded access to public resources, without the program running state switch, from the user state into the kernel state, with the operating system for mutual exclusion.
In the
--->JVM specification, you can see that synchronized is implemented in the JVM, and the JVM implements the method synchronization and code blocks based on entering and exiting the monitor object. At the beginning of the code synchronization, the Monitorenter is woven into the monitorexit instruction implementation at the end of the synchronization position (normal end and exception end). When the thread executes to Monitorenter, it takes ownership of the monitor corresponding to the lock object lock, which is the attempt to acquire the lock of the object. (Any object has another monitor associated with it, and when a monitor is held, he is locked)
--->java multithreading security is based on the lock mechanism, and lock performance is often unsatisfactory. The reason is that Monitorenter and Monitorexit, the two bytecode primitives that control multithreading synchronization, are implemented by the JVM relying on operating system mutexes (mutexes).
---> Mutex is a resource-intensive operation that causes a thread to hang and, in a short time, needs to be re-dispatched back to the original thread.
---> In order to optimize the lock mechanism of Java, the concept of lightweight locking is introduced from JAVA6. Lightweight locking is intended to reduce the chance of multi-threaded access to mutual exclusion, not to replace mutual exclusion. It leverages the CPU primitive Compare-and-swap (CAS, assembly instruction Cmpxchg), and attempts to remedy it before attempting to enter the mutex.
Second: Why Spin or adaptive spin? ---> Before we discussed mutual exclusion synchronization, it was mentioned that the maximum performance effect of mutex synchronization is the implementation of blocking, and the operation of suspending thread and recovery thread need to be completed in the kernel state, which brings great pressure to the concurrency performance of the system. At the same time, the development team of the virtual machine also noticed that in many applications, the lock state of shared data would only last for a short period of time, and it was not worthwhile to suspend and resume threads for this period of time. If the physical machine has more than one processor, allowing two or more threads to execute concurrently simultaneously, we can let the thread that asks for the lock "wait a while", but not abandon the processor's execution time and see if the thread holding the lock will release the lock soon. In order for the thread to wait, we only have to let the thread perform a busy loop (spin), the technology is called spin lock.
---> Spin locks have been introduced in JDK 1.4.2, but are turned off by default and can be turned on using the-xx:+usespinning parameter, which has been turned on by default in JDK 1.6. Spin wait can not replace blocking, and do not say the number of processors, the spin wait itself although the cost of the thread switching, but it is to take up the processor time, so if the lock is occupied for a short time, the spin-wait effect will be very good, conversely, if the lock is occupied for a long time, Then the spinning thread will simply consume the processor resources without doing any useful work, but will result in a waste of performance. Therefore, the time of the spin wait must have a certain limit, if the spin exceeds the limit number of times still did not successfully obtain the lock, you should use the traditional way to hang up the thread. The default value of the spin count is 10 times, and the user can change it using the parameter-xx:preblockspin.
---> Introduced self-adaptive spin locks in JDK 1.6. Adaptive means that the spin time is no longer fixed, but is determined by the previous spin time in the same lock and the state of the lock owner. If, on the same lock object, the spin wait has just been successfully acquired, and the thread holding the lock is running, the virtual machine will assume that the spin is likely to succeed again, and that it will allow the spin wait to last for a relatively longer period, such as 100 loops. On the other hand, if the spin is rarely successfully obtained for a lock, it may be possible to omit the spin process later to avoid wasting processor resources. With adaptive spin, as the program runs and the performance monitoring information continues to improve, virtual machines will become more and more accurate in the state of the program lock, and the virtual machine becomes more and more "smart".
Three: Lock removal---> Lock removal refers to the virtual machine instant compiler, which requires synchronization on some code at run time, but locks that are detected as impossible to compete for shared data are clipped. The main determinant of the lock removal is based on data support from the Escape analysis (the 11th chapter has explained the escape analysis technology), if the judgment in a piece of code, all the data on the heap will not escape to be accessed by other threads, it can be treated as data on the stack, think that they are thread-private, Synchronous locking naturally does not need to be carried out.
---> Maybe the reader will have questions about whether the variable is escaping, which needs to be determined by the data flow analysis for the virtual machine, but the programmer should be very clear about it, how does it require synchronization to know that there is no data contention? The answer is that there are many synchronization measures that are not added by the programmer himself, and that the synchronization of the code in Java programs may be more prevalent than most readers might imagine. For example: (just illustrate the concept, but the reality is not necessarily as an example) in a thread-safe environment using StringBuffer for string spelling. When the Java file is compiled, the lock pin is removed.
Four: Lock coarsening
---> In principle, when we write code, we always recommend limiting the scope of the synchronization block to be as small as possible-synchronizing only in the actual scope of the shared data, so that the number of operations that need to be synchronized is as small as possible, and if there is a lock contention, the thread that waits for the lock can get the lock as quickly as possible.
---> In most cases, the above principles are correct, but if a series of successive operations are repeatedly locked and unlocked for the same object, even the lock operation appears in the loop body, even if there is no thread competition, frequent mutex synchronization operations can lead to unnecessary performance loss. ---> If a virtual machine detects a string of fragmented operations that lock the same object, it will extend the scope of the lock synchronization (lock coarsening) to the outside of the entire sequence of operations.
V: Bias Lock, lightweight lock, weight lock contrast
Lock |
Advantages |
Disadvantages |
Applicable scenarios |
Biased lock |
Locking and unlocking does not require additional consumption, and there is only a nanosecond-level gap compared to performing a non-synchronous method |
If there is a lock contention between threads, additional lock revocation consumption is brought |
Applies to only one thread to access the synchronization block scene |
Lightweight lock |
Competing threads do not block, increasing program responsiveness |
If you never get a thread competing, using spin consumes the CPU |
Fast response speed, synchronous block execution speed |
Heavy-weight lock |
Thread contention does not use spin and does not consume CPU |
Thread blocking, slow response time |
Faster synchronization block execution with throughput |
Storage content for object Headers (monitor)
Length |
Content |
Description |
32/64bit |
Mark Word |
Store Hashcode or lock information for an object |
32/64bit |
The address of the class object |
Pointer to data stored in object type |
32/64bit |
Array length |
The length of the array (if the current object is an array) |
status changes for Mark Word store content (monitor)
Lock status |
25bit,4bit |
1bit (whether it is a bias lock) |
2bit (lock Mark Bit) |
Lightweight lock |
Pointer to lock record in stack |
|
00 |
Heavy-weight lock |
Pointer to mutex (heavyweight lock) |
|
10 |
Gc |
Empty |
|
11 |
Biased lock |
Thread ID, Object Hashcode, object generational age |
1 |
01 |
Six: The state of the lock
---> Lock a total of four states (from low to High order): No lock state, biased to lock state, light-weight lock state, heavy lock status---> lock level can only be upgraded, can not be downgraded. This lock escalation strategy is not degraded, and is designed to improve the efficiency of acquiring locks and releasing locks.
Seven: Biased lock---The >A thread obtains the lock and creates a lock record (lock recording variable) in the stack frame of the A thread, the thread ID of the a thread is stored in the object header of the lock object and in the lock record. The subsequent entry of the thread does not require CAS operations, just to determine whether it is the current thread. ---The >a thread acquires the lock and does not release the lock. The A thread will not release the lock until the B thread also competes for the lock. ---> Favors the release of the lock, waits for the global security point (at which point there is no byte code executing), it pauses the thread that holds the biased lock first, and then checks to see if the thread holding the biased lock is still alive, and if the thread is not active, set the object header to a lock-free state. If the thread is still alive, a stack with a biased lock will be executed, traversing the record of the biased object. The lock record in the stack frame and the object header mark Word either re-leans to the other thread, either reverts to no lock, or the tagged object does not fit as a biased lock. Finally wakes the paused thread. ---> Turn off the biased lock,-xx:usebiasedlocking=false by the JVM's parameter, the lightweight lock is entered by default.
Eight: Lightweight lock---The >a thread acquires the lock, it creates a lock record (lock recording variable) in the stack frame of the A thread, and the lock record pointer points to mark Word in the object header of the lock object. Then mark Word points to the lock record. This is the acquisition of the lock. ---> Lightweight lock, b thread in the lock competition, the discovery lock has been occupied by a thread, the B thread does not enter the kernel state, let the b thread spin, execute an empty loop, wait for a thread to release the lock. If the spin strategy is complete, the A thread does not release the lock, or the C thread is occupied. The B thread attempts to upgrade the lightweight lock to a heavyweight lock.
---> Lock record (Lockrecord) and Object header (mark Word) for pointer exchange
10: Heavy-lock---> Heavyweight lock, which is to convert a thread that competes for locks from a user state to a kernel state. Let the CPU use the operating system for thread coordination.
Multi-thread: bias lock, lightweight lock, heavyweight lock