"Dead java Concurrency"-----In-depth analysis of the implementation principle of volatile

Source: Internet
Author: User
Tags volatile

We learned from the previous chapter that synchronized is a heavyweight lock, and although the JVM has done a lot of optimizations for it, the volatile described below is a lightweight synchronized. If a variable uses volatile, it is less expensive than using synchronized because it does not cause thread context switching and scheduling. The Java language Specification defines volatile as follows:

The Java programming language allows threads to access shared variables, and in order to ensure that shared variables can be updated accurately and consistently, the thread should ensure that the variable is obtained separately through an exclusive lock.

The above-mentioned, the popular point is that if a variable is modified with volatile, then Java can ensure that all threads see the value of this variable is consistent, if a thread updates a volatile modified shared variable, then other threads can immediately see the update, This is known as thread visibility.

Volatile, although it seems simple to use is nothing more than a variable in front of the volatile can be, but it is not easy to use (LZ admits that I still use the bad, in use is still ambiguous).

Memory model related Concepts

Understanding the volatile is still a bit difficult, it is related to the Java memory model, so before we understand volatile we need to understand the concept of Java memory model, here is only a preliminary introduction, the following LZ will detail the Java memory model.

Operating system semantics

When the computer runs the program, each instruction is executed in the CPU, and it is bound to involve the reading and writing of the data during the execution. We know that the program running data is stored in main memory, then there will be a problem, read and write main memory data is not executing instructions in the CPU fast, if any interaction needs to deal with main memory will greatly affect the efficiency, so there is a CPU cache. The CPU cache is unique to a CPU and is only relevant for threads that are running on that CPU.

Having a CPU cache solves the problem of efficiency, but it brings up a new problem: data consistency. In the program run, will run the data required to replicate to the CPU cache, in the operation of the CPU no longer deal with main memory, but directly from the cache to read and write data, only when the end of the run will be the data flushed to main memory. To give a simple example:

i++i++

When the thread runs the code, I (i = 1) is read from main memory, then a copy is copied to the CPU cache, then the CPU executes + 1 (2), then the data (2) is written to the tell cache and finally flushed to main memory. In fact, this is not a problem in a single thread, the problem is in multi-threading. As follows:

If there are two threads A and b performing this operation (i++), according to our normal logical thinking, the I value in main memory should be = 3, but is it true? The analysis is as follows:

Two threads read the value of I in main memory (1) to the respective cache, then thread a performs a +1 operation and writes the result to the cache, and finally to main memory, at which time the main memory i==2, thread B does the same, and I in main memory is still = 2. So the final result is 2 not 3. This behavior is a cache consistency issue.

There are two ways to resolve a cache consistency scenario:

    1. By adding a lock# lock on the bus
    2. By caching the consistency protocol

However, there is a problem with scenario 1, it is implemented in an exclusive way, that is, the bus plus lock# lock, only one CPU can run, the other CPUs are blocked, inefficient.

The second scenario, the Cache Consistency Protocol (MESI protocol), ensures that a copy of the shared variable used in each cache is consistent. Its core idea is as follows: When a CPU is writing data, if the variable that finds the action is a shared variable, it informs the other CPU that the cached row of the variable is invalid, so when the other CPU reads the variable, it discovers that its invalidation will reload the data from main memory.

Java memory model

Above from the operating system level to explain how to ensure data consistency, let's look at the Java memory model, a little bit of the Java memory model to provide us with what guarantees and in Java provides the methods and mechanisms to enable us in the implementation of multi-threaded programming to ensure the correctness of program execution.

In concurrent programming we typically encounter these three basic concepts: atomicity, visibility, and ordering. Let's take a little look at the volatile

Atomic Nature

Atomicity: That is, one operation or multiple operations are either executed completely and the execution process is not interrupted by any factor, or it is not executed.

Atomicity is like a transaction in a database, they are a team, die. In fact, the understanding of atomicity is very simple, let's look at a simple example:

i = 0;            ---1j = i;            ---2i++;            ---3i = j + 1;    ---4

Of the above four operations, which are the atomic operations, and which ones are not? If not very understanding, it may be considered atomic operation, in fact, only 1 is the atomic operation, the rest is not.

1-in Java, variables and assignment operations on basic data types are atomic operations;
2-contains two operations: Read I, assign I value to J
3-Contains three operations: reads I, i + 1, assigns +1 results to I;
4-Same as three

In a single-threaded environment, we can assume that the entire step is atomic, but in a multithreaded environment, Java only guarantees that the basic data type of variables and assignment operations is atomic ( Note: In a 32-bit JDK environment, the reading of 64-bit data is not atomic operations *, such as Long, Double). To ensure atomicity in a multi-threaded environment, it can be ensured by locks and synchronized.

Volatile is no guarantee of the atomicity of composite operations

Visibility of

Visibility means that when multiple threads access the same variable, a thread modifies the value of the variable, and other threads can immediately see the modified value.

It has been analyzed above that in a multithreaded environment, the operation of a thread on a shared variable is not visible to other threads.

Java provides volatile to ensure visibility.

When a variable is modified by volatile, it indicates that the thread local memory is invalid, and when a thread modifies the shared variable, he is immediately updated to main memory, and when the other thread reads the shared variable, it reads directly from the main memory.
Of course, synchronize and locks can guarantee visibility.

Order of

Ordering: The order in which the program executes is executed in the order of the Code.

In the Java memory model, in order to be efficient, the compiler and processor are allowed to reorder instructions, and of course reordering it does not affect the results of single-threaded operation, but it has an impact on multithreading.

Java provides volatile to ensure a certain order. The most notable example is the DCL (double check lock) inside the singleton pattern. Here the LZ is no longer elaborated.

Analyze the volatile principle

JMM is quite large, not a little on the above can be elaborated. The above simple introduction is for the volatile to pave the way.

Volatile can guarantee thread visibility and provide some order, but it cannot guarantee atomicity. The underlying volatile of the JVM is implemented using a "memory barrier".

There's two levels of semantics in that passage.

    1. Guaranteed visibility, no guarantees of atomicity
    2. Prohibit command reordering

The first layer of semantics is not introduced, the following emphasis on instruction reordering.

In order to improve performance when executing a program, the compiler and processor usually reorder the instructions:

    1. The compiler resets the ordering. The compiler can rearrange the execution order of the statements without changing the semantics of the single-threaded procedure;
    2. The processor is re-ordered. If there is no data dependency, the processor can change the execution order of the statement corresponding to the machine instruction;

The command reordering has no effect on the single thread, and he will not affect the results of the program, but will affect the correctness of multithreading. Since command reordering affects the correctness of multithreaded execution, we need to prohibit reordering. So how does the JVM prohibit reordering? This question is answered later, we first look at another principle Happens-before,happen-before principle guarantees the "order" of the program, it stipulates that if the order of execution of two operations cannot be pushed out from the happens-before principle, Then they cannot guarantee order and can reorder them arbitrarily. It is defined as follows:

    1. The previous operation in the same thread happen-before the subsequent operation. (That is, the single thread executes in code order.) However, it is legal to reorder the compiler and processor without affecting the execution of the results in a single-threaded environment. In other words, this is a rule that does not guarantee the compilation rearrangement and the command reflow.
    2. The unlock operation on the monitor happen-before its subsequent lock operation. (Synchronized rules)
    3. The write operation of the volatile variable happen-before the subsequent read operation. (volatile rules)
    4. The start () method of the thread Happen-before all subsequent operations on that thread. (Thread start rule)
    5. All the operations of the thread Happen-before other threads on that thread call the join after the successful operation is returned.
    6. If a happen-before b,b happen-before C, then a happen-before C (transitive).

We focus on the 3rd volatile rule: Write operations on volatile variables Happen-before subsequent read operations. In order to implement volatile memory semantics, JMM is reordered with the following rules:

With a little bit of understanding of the Happen-before principle, let's answer that question. How does the JVM prohibit reordering?

observing the addition of the volatile keyword and the assembly code generated when the volatile keyword was not added, a lock prefix instruction is added when adding the volatile keyword . The lock prefix instruction is actually equivalent to a memory barrier. A memory barrier is a set of processing instructions used to implement a sequential limit on memory operations. The bottom of the volatile layer is realized through the memory barrier. is the memory barrier required to complete the above rules:

Volatile for the time being analyzed here, the JMM system is relatively large, not few words can be said clearly, the latter will be combined with JMM again in-depth analysis of volatile.

Summarize

Volatile looks simple, but it's still difficult to understand, and it's just a basic understanding of it. Volatile is slightly lighter than synchronized, and in some cases it can replace synchronized, but it does not completely replace synchronized, which can only be used on certain occasions. Use it must meet the following two conditions:

    1. The write operation of the variable does not depend on the current value;
    2. The variable is not contained in an invariant that has other variables.

Volatile is often used in two or two scenarios: a status tag, double check

Resources
    1. Zhou Zhiming: "In-depth understanding of Java virtual machines"
    2. Fang Fei: "The Art of Java concurrent programming"
    3. Java concurrency Programming: volatile keyword parsing
    4. Java concurrency Programming: the use of volatile and its principles

"Dead java Concurrency"-----In-depth analysis of the implementation principle of volatile

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.