Deep understanding of Java synchronization and locking mechanisms

Source: Internet
Author: User
Tags cas instance method mutex volatile

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

We all know that the code that is synchronized decorated in Java is called a synchronized code block, which means that only one thread executes at the same time, and that other threads are excluded from the synchronization block, and that access is executed in some order. The synchronized is actually based on the monitor, and each instance and class has a monitor, and usually the "lock" action is to get the monitor. So we usually say that synchronized is based on the JVM level and uses the lock that is built into the object. The static method locks the monitor for the class, and the instance method locks the monitor for the corresponding instance. Synchronization is implemented using the Monitorenter and Monitorexit directives, and Monitorenter attempts to acquire the lock of the object, if the object is not locked or when the thread has acquired the lock, the counter of the lock is +1, the same monitorexit the counter of the lock-1. Therefore synchronized is reentrant for the same thread.

The monitor supports two threads: mutual exclusion (sync) and collaboration. Java implements mutually exclusive access to critical areas through the lock of objects, using the Wait () of object, notify (), Notifyall () method.

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 some modification or other operation it thinks that there will be no other thread to do the same (competition), which is an optimistic attitude, usually based on the CAS atomic directives. About CAs you can see this article Java and contract the CAS operations, CAS typically do not suspend threads, so sometimes performance is better. (thread switching is a very performance-intensive operation).

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

The previous synchronized would have blocked the thread, that is, context switches, from the user state to the kernel state, because this method is sometimes too resource-intensive, so then there is a spin lock, the so-called spin is actually if the lock has been occupied by other threads, the current thread does not hang, Instead of shorting the operation, spin is actually optimistic in a way because it always thinks it will get locked next time. The spin lock is therefore suitable for use in less competitive situations, and it is understood that the current JVM has been optimized for synchronized.

The use of spin is also divided into scenes, it is possible that the thread spin for a long time did not acquire the lock, then the CPU is wasted, it is better to suspend 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 the spin.

Lightweight locks

Lightweight locks are the concept of a heavy lock relative to a mutex operation, and lightweight locks are designed to reduce the likelihood of mutex for multiple threads, not to replace mutexes. To understand the lightweight lock and the bias lock that follows, you must first understand the memory layout of the object header. The following diagram is the memory layout of the object header:


The initial 01 means no locks, 00 for lightweight locks, 10 for heavyweight locks, and so on. When the code enters the sync block, if the synchronization object is not locked (the lock flag bit is "01"), the virtual machine first creates a space in the current thread's stack frame called the lock record, which stores the lock object. Current Mark Copy of Word (the official adds a displaced prefix to the copy, that is, displaced Mark Word), and then the virtual machine tries to use the CAS operation to point the object's lightweight pointer to the stack's lock record, and if the update succeeds the current thread acquires the lock, and marked for 00 lightweight locks. If this update operation fails, the virtual machine first checks whether the object's mark word points to the current thread's stack frame, and if it shows that the current thread already has the lock on the object, it can proceed directly into the synchronization block, otherwise the lock object has been preempted by another thread. If there are more than two threads competing for the same lock, the lightweight lock is no longer valid, to inflate to a heavyweight lock, the status value of the lock flag becomes "Ten", Mark Word stores a pointer to a heavyweight lock (mutex), and a thread that waits for the lock to go into a blocking state.

Bias Lock

The bias of the lock is the meaning of the eccentric, when the lock was first acquired by a thread, the thread ID of the lock is acquired in the object header record, and every time the thread enters the synchronization block, it does not need to be locked, and if another thread acquires the lock, the preference lock mode fails, the lock is revoked back to an unlocked or lightweight lock state. The function of the bias lock is to completely eliminate the lock, even the CAS operation is not done.

Let's look at the transition of a thread into the synchronized block and the state of the synchronized block.

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

Contention list: All threads that request a lock will be placed first in the competition queue Entry list:contention list those eligible to become candidates are moved to Entry list wait Set: Those threads that invoke the wait method to be blocked are placed in the wait Set OnDeck: At any time there can be at most one line one thread in the competitive lock, the thread called OnDeck owner: the thread that gets the lock is called owner! Owner: The thread that releases the lock below is a picture of a netizen drawing very image:


The newly requested thread is placed in the contentionlist, and when one of the owner releases the lock, the owner moves the thread from contentionlist to Entrylist if Entrylist is empty. Obviously, the contentionlist structure is actually a lock-free queue, because only owner will fetch the node from the contentionlist.

Entrylist and contentionlist are logically the same waiting queues, and contentionlist are concurrently accessed by threads, creating entrylist in order to reduce contention for the tail of the Contentionlist team. The owner thread migrates threads to entrylist from contentionlist when unlock, and specifies that a thread (typically head) in Entrylist is a ready (OnDeck) thread. The owner thread does not pass the lock to the OnDeck thread, but simply gives the ondeck,ondeck thread the right to compete for the lock. Although this has sacrificed a certain fairness, but greatly improved the overall throughput, in the hotspot of the ondeck choice of behavior called "competition switching."

Lock can be reentrant

The biggest benefit of reentrant locks is that you can avoid thinking, because for the thread that has acquired the lock, you don't need to get the lock again, just use the counter +1, which is actually a synchronized lock. But what we are going to talk about in this section is the Reentrantlock in concurrent packages and their 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, which is in the Java.util.concurren package.

Let's take a look at the difference between the lock provided by the JVM and the lock in the contract:

The lock and release of 1.synchronized is provided by the JVM and does not require our attention, and lock lock and release are all controlled by us, and usually the action to release the lock is implemented in finally.

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

The 3.ReentrantLock has the same concurrency and memory semantics as synchronized, in addition to locking ballots, timed locks waiting and interrupt lock waiting.

Before explaining the Reentrantlock, first look at the Atomicinteger source code general understanding of its implementation principle.

/** * atomically increments by one of the current value.
     * * @return The updated value///This method is similar to the synchronized version of i++, first the current value of +1, and then return,///Can see is a for loop, only when Compareandset succeeds will return
    So when does it work? Public final int Incrementandget () {for (;;) {int current = get (); variable of type//volatile, so each fetch is the latest value int next = current + 1;//plus 1 operation if (CO Mpareandset (current, next))//The key is the method in if//if Compareandset succeeds, the whole operation succeeds, if it fails, then another thread has modified the value//then the next round of add 1 operation until the
        Work return next;
     }/** * Gets the current value. * * @return The current value///get method is simple and returns value, which is the member variable of the class and is the volatile public final int get ()
    {return value;  }/** * atomically sets the "value to" given updated value * If the current value {@code = =} The expected
     Value. * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value is not equal to the expected value. /Public Final Boolean compareandset (int expect, int update) {//Continue tracking unsafe method, found not provided, actually this method is a local class library based atomic method, using a
	Instructions 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, update the value to the expected value, return success, or return failure returns Unsafe.compareandswapint (this, valueoffset, expect,
    Update); }
It can be foreseen that if the competition is very fierce, the probability of failure will be greatly increased and performance will be affected. In fact, the most of the lock in the contract is based on CAS operation, this section intends to explain can be locked, but need to know a lot of things, had to write a new article to introduce Reentrantlock.
Related Article

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.