In-depth understanding of Java synchronization, lock mechanism

Source: Internet
Author: User
Tags cas

This article attempts to understand our common synchronization (synchronized) and lock mechanisms from another level. If the reader wants to learn more about concurrency, recommend a book, "Java concurrency Programming in action," a very classic book, English proficiency of students can also read the Concurrent programming in java-design principles and Patterns "by Doug Lea himself, Doug Lea is the great god of concurrency, and the JDK's concurrent package is done by him.

We all know that code that is synchronized decorated in Java is called a synchronous code block, which means that only one thread executes at the same time, other threads are excluded from the synchronization block, and access is executed in some order. In fact, synchronized is a monitor-based implementation, and each instance and class has a monitor, which is usually what we call the "lock" action to get the monitor. So usually we talk about synchronized based on the JVM level, using the lock built into the object. The static method locks the class's monitor, and the instance method locks the monitor for the corresponding instance. Synchronization is implemented using the Monitorenter and monitorexit instructions, Monitorenter attempts to get the lock on the object, if the object is not locked or when the front thread has acquired the lock, then the lock counter +1, the same monitorexit the lock counter-1. So synchronized is reentrant for the same thread.

The monitor supports two types of threads: mutual exclusion (sync) and collaboration. Java uses object locks to implement mutually exclusive access to critical sections, using the Wait (), notify (), Notifyall () method of object.

Optimistic lock and pessimistic lock

These two names have appeared in many places, and the so-called optimistic lock is that when it comes to making a change or other operation, it thinks there will be no other threads to do the same (competition), which is an optimistic attitude, usually based on CAS atomic directives. About CAs you can see this article in Java and contract the CAS operations, CAS usually do not hang threads, so sometimes performance is better. (thread switching is an operation that consumes performance).

Pessimistic lock, according to the definition of optimistic lock is easy to understand pessimistic lock is that there must be other threads competing for resources, so regardless of whether there will be no contention, pessimistic locks will always first to lock resources.

Previous synchronized are blocked threads, that is, context switches, from the user state to the kernel state, because this way is sometimes too resource-intensive, and then there is a spin lock, so-called spin is actually if the lock has been occupied by other threads, the current thread does not hang, It's a short operation, and the spin is, in a way, an optimistic lock, because it always thinks it will get locked next time. Spin locks are therefore suitable for use in situations where competition is not intense, and it is understood that the current JVM has been optimized for synchronized.

Spin use is also sub-scene, it is possible that the thread spins for a long time did not acquire the lock, then the CPU is wasted, it is not as good as suspending the thread, so there is an adaptive spin lock, it will be more historical spin whether to obtain the record of the lock to determine the spin time or whether to spin.

Lightweight lock

The concept of lightweight locking is relative to the need for mutex operation of the heavyweight lock, the purpose of the lightweight lock is to reduce the mutual exclusion of multithreading, not to replace mutual exclusion. To understand the lightweight lock and the bias lock that follows, you must first understand the memory layout of the object's head. The following figure is the memory layout of the object header:


The initial 01 means no lock, 00 represents a lightweight lock, 10 is a heavyweight lock, and so on. When the code enters the synchronization block, if the synchronization object is not locked (the lock flag bit is a "01" state), the virtual machine will first establish a space named lock record in the stack frame of the current thread for storing the lock object the current mark Copy of Word (the official adds a displaced prefix to this copy, i.e. displaced Mark Word), and the virtual machine attempts to use the CAS operation to point the object's lightweight pointer to the lock record of the stack, if the update succeeds the current thread acquires the lock, and marked as 00 Lightweight lock. If this update fails, the virtual machine first checks to see if the object's mark word points to the stack frame of the current thread, and if it means that the current thread already has a lock on the object, then it can go straight to the synchronization block, or the lock object is already preempted by another thread. If more than two threads are contending for the same lock, then the lightweight lock is no longer valid, to be inflated to a heavyweight lock, the status value of the lock flag is changed to "Ten", and Mark Word stores a pointer to a heavyweight lock (mutex), and the thread that waits for the lock goes into a blocking state.

Biased lock

The bias lock is the meaning of eccentricity, when the lock is first acquired by a thread, the thread ID of the lock is obtained in the object header record, and every time the thread enters the synchronization block, it does not need to lock, and if any other thread acquires the lock, the bias lock mode fails, and the lock is withdrawn back to the unlocked or lightweight lock state. The effect of a biased lock is to completely eliminate the lock, even if the CAS operation is not done.

Let's take a look at the state transitions in the synchronization block and out-of-sync blocks in a thread.

When multiple threads request an object monitor at the same time, object monitor sets several states to differentiate the requested thread:

    • Contention List: All threads that request a lock will be placed first in the competition queue
    • Entry list:contention List The threads that qualify as candidates are moved to the Entry list
    • Wait set: Those threads that call the wait method to be blocked are placed into the wait set
    • OnDeck: There can be at most one line is impersonating at a competitive lock at any time, the thread is called OnDeck
    • Owner: The thread that acquired the lock is called owner
    • ! Owner: The thread that freed the lock
The following is a netizen drawing very image:


The newly requested thread is placed into the contentionlist, and when an owner releases the lock, if entrylist is empty, the owner moves the thread from Contentionlist to Entrylist. Obviously, the contentionlist structure is actually a lock-free queue, because only the owner takes the node from Contentionlist.

Entrylist and contentionlist logically belong to the waiting queue, contentionlist will be accessed concurrently by the thread, in order to reduce contention for contentionlist team tail, and establish entrylist. The owner thread migrates the thread from contentionlist to entrylist when unlock, and specifies that a thread in entrylist (typically head) is a ready (OnDeck) thread. The owner thread is not passing the lock to the OnDeck thread, but handing over the right to the competition lock to the Ondeck,ondeck thread requires a re-competition lock. While this sacrifices some fairness, it greatly improves the overall throughput and calls OnDeck's choice behavior "competitive switching" in the hotspot.

Can be re-entered lock

The biggest benefit of the reentrant lock is to avoid thinking, because for the thread that has acquired the lock, there is no need to get the lock again, only the counter +1 can be, in fact, synchronized is also a kind of reentrant lock. But what we're going to talk about in this section is the Reentrantlock in the concurrency package and its implementation. Synchronized is a lock provided at the JVM level, and the JDK at the Java language level provides us with a very good lock, all in the Java.util.concurren package.

Let's take a look at the differences between the locks provided by the JVM and the locks in the bundle:

1.synchronized locking and release are provided by the JVM, do not need our attention, and the lock locking and release are all controlled by us, usually release the lock action to be implemented in finally.

2.synchronized has only one status condition, that is, each object has only one monitor, if you need multiple condition combinations then synchronized is not satisfied, and lock provides a multi-conditional mutex, very flexible.

The 3.ReentrantLock has the same concurrency and memory semantics as synchronized, plus more lock polls, timed lock waits, and interrupt lock waits.

Before explaining the Reentrantlock, take a look at the Atomicinteger source code generally understand the implementation of the principle.

/** * atomically increments by one of the current value. * * @return The updated value *//the method is similar to the synchronous version of i++, the current value of +1, and then return,//can be seen as a for loop, only if Compareandset success will return/    /Then when did it succeed? Public final int Incrementandget () {for (;;) {int current = get ();//volatile type of variable, so each fetch is the newest value int next = 1;//plus 1 operation if (Compa                Reandset (current, next))//The key is the If method//If Compareandset succeeds, the whole plus operation succeeds, if it fails, then another thread has modified value//Then the next round of add 1 operation until successful        return next;     }}/** * Gets the current value.        * * @return The current value *///get method is very simple, return value, this value is a member of the class variable, and is volatile public final int get () {    return value; }/** * Atomically sets the value to the given updated value * If the current value {@code = =} The expected Val     Ue. * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual VALue was wasn't equal to the expected value. */Public final Boolean compareandset (int expect, int update) {///Continue tracking unsafe method, found not provided, actually this method is an atomic method based on the local class library, using a reference To complete the operation. If the value in memory is the same as the expected value, that is, no other thread has modified the value, the value is updated to the expected value, the return succeeds, or the return failure returns Unsafe.compareandswapint (this, valueoffset, expect,    Update); }
Predictably, if the competition is intense, the probability of failure is greatly increased and performance is affected. In fact, the locks in the contract are mostly based on CAS operations, this section intends to explain the re-entry lock, but need to know a lot of things, had to re-write an article to introduce Reentrantlock.

In-depth understanding of Java synchronization, lock mechanism

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.