Java basics of Java perplexed-volatile

Source: Internet
Author: User

volatile is widely used in multi-threaded concurrency because it has two features:

    1. The visibility of this variable is ensured by a thread that modifies the value of a variable, which is immediately visible to other threads.
    2. command reordering is prohibited.
related concepts of memory models

To understand the principle of volatile, you first need to know some concepts of the memory model.

When the computer executes the program, each instruction is executed in the CPU, while the execution of the instruction is bound to involve the reading and writing of the data. Since the temporary data in the program is stored in main memory (physical RAM), there is a problem, because the CPU is executing fast, and the process of reading data from memory and writing data to memory is much slower than the CPU executing instructions. Therefore, if the operation of the data at any time through the interaction with the memory, it will greatly reduce the speed of instruction execution. So there's a cache in the CPU.

That is, when the program is running, the data required for the operation is copied from the main memory to the CPU cache, then the CPU can be calculated directly from its cache to read data and write data to it, when the end of the operation, then the cache of data flushed to main memory. A simple example, such as the following code:

countcount1;

When the thread executes this statement, the value of I is read from main memory and then copied to the cache, then the CPU executes the instruction to add 1 to I, then writes the data to the cache, and finally flushes the most recent value of I in cache to main memory.

There is no problem with this code running in a single thread, but running in multi-threading can be problematic. In multi-core CPUs, each thread may run on a different CPU, so each thread runs with its own cache (this is actually the case for a single-core CPU, but it is performed separately in the form of thread scheduling).

To address the problem of cache inconsistencies, there are generally 2 workarounds:

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

These 2 approaches are available at the hardware level.

In the early CPU, the problem of cache inconsistency was solved by adding lock# lock on the bus. Because the CPU and other components communicate through the bus, if the bus plus lock# lock, that is, blocking other CPU access to other parts (such as memory), so that only one CPU can use this variable memory. For example, if a thread executes i = i +1 in the above example, if a lcok# lock is signaled on the bus during the execution of this code, then the other CPU can read the variable from the memory where the variable I resides and then perform the appropriate operation only after the code is fully executed. This solves the problem of cache inconsistency.

However, there is a problem with the above approach because the other CPUs are unable to access the memory during the locking of the bus, resulting in inefficiency.

So there is a cache consistency protocol. The best known is the Intel Mesi protocol, which guarantees that a copy of the shared variables used in each cache is consistent. The core idea is that when the CPU writes the data, if the variable that is found to be an action is a shared variable, that is, a copy of the variable exists in the other CPU, a signal is signaled to the other CPU that the cache row of the variable is invalid, so that when the other CPU needs to read the variable, The cache line that caches the variable in its own cache is not valid, and it is re-read from memory.

three concepts in concurrent programming
    1. atomicity , that is, an operation or operations that are executed in full or executed, are not interrupted by any factor, or are not executed;
    2. visibility , refers to when multiple threads access the same variable, a thread modifies the value of the variable, and other threads can immediately see the modified value;
    3. Ordering, that is, the order in which the program executes is executed in the order of the Code.

For ordering, here is a knowledge point instruction reordering.

Order reordering

What is Order reordering? Simply put: Assume that the code has two statements, the code order is statement 1 is executed before statement 2, so long as the statement 2 does not depend on the results of statement 1, the order to disrupt them has no effect on the final result, then the actual CPU to execute, their order can be unlimited.

int10;    //语句1int2;    //语句23;    //语句3r = a*a;     //语句4

The simple understanding is that you can allow statement 2 to be executed by the CPU before statement 1, and in the order of the Code. Sometimes we also need to prevent order reordering, and all have the happens-before principle.

Happens-before Principles

Let's look at a multi-threaded code:

 Public  class Test1 {     Private intA=1, b=2; Public void Foo(){//thread 1A=3; b=4; } Public int Geta(){//Thread 2        returnA } Public int Getb(){//Thread 2        returnb } }

The above code, when thread 1 executes the Foo method, what will be the results of thread 2 accessing Geta and GETB?
Answer:
A:a=1, b=2//have not changed
B:a=3, b=4//has changed.
C:a=3, b=2//A has changed, B has not changed
D:a=1, b=4//b has changed, A has not changed
The above a,b,c are well understood, but D may be unexpected to some people.
This is an issue in which the memory visibility (Visibility) order is inconsistent between multiple threads. There are two possible causes of the above D option.

    1. The Java compiler's reordering (reording) operation may lead to inconsistent execution order and code order;
    2. The order cannot be guaranteed when writing back to main memory from a thread's working RAM.

One of the important questions in JMM is how to make the state of an object consistent with the "visibility" of each thread between multiple threads. Its solution is to happens-before the rules:
JMM defines a partial-order relationship for all program internal actions, called Happens-before. To ensure that the thread executing action B sees the result of action a (whether A and b occur in the same thread), the happens-before relationship must be met between A and B.

Let's take a look at what the "Happens-before" rules are (excerpt from the Java Concurrency Programming Practice):

① Program Order rule: Every action A in a thread is happens-before to every action B in that thread, where all action B can appear after a in the program.
② Monitor Lock rule: Unlocking a monitor lock happens-before every subsequent locking to the same monitor lock.
③volatile Variable Law: Write Operations on volatile fields happens-before each subsequent read and write operation to the same domain.
④ thread start rule: In one thread, the call to Thread.Start is happens-before to the action of each startup thread.
⑤ thread termination rule: Any action in the thread is happens-before to other threads that the thread has been terminated, or returned successfully from the Thread.Join call, or Thread.isalive returns FALSE.
⑥ Interrupt rule: One thread calls the interrupt of another thread happens-before the interrupted thread discovers the interrupt.
⑦: The end of an object's constructor Happens-before at the beginning of the object finalizer.
⑧ transitivity: If a happens-before to B, and B Happens-before to C, a happens-before to C

Before Java5, JMM's definition of volatile was: to ensure that read-write volatile occurs directly in main memory, the thread's working memory is not cached. it only promises to read and write the visibility of the process, and does not limit the reording, so the old volatile is not too reliable. After Java5, JMM's semantics of volatile were enhanced. Is the law of ③volatile variables we see.
How do you understand the "atomized operation"? Look at the following example:

privatestaticvolatileint0;publicstaticintgenerateSerialNumber(){    return nextSerialNum++;}

The above code uses volatile to modify the nextserialnum, according to the law of the third volatile variable of the previous "Happens-before" law, it seems that different threads will get a new serialnumber
The problem is that the nextserialnum++ statement, which is not an atomized one , is actually read-modify-write three operations, which could make thread 1 before write, Thread 2 also accesses the Nextserialnum, causing thread 1 and thread 2 to get the same serialnumber.
Therefore, when using volatile, you need to be aware

    • No mutex required;
    • The change in the state of the object is not atomized.
the principle and realization mechanism of volatile

Let's look at how volatile guarantees visibility and suppresses order reordering.

The following is an excerpt from the in-depth understanding of Java Virtual machines:

"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 (also a memory fence), and the memory barrier provides 3 functions:

    1. It ensures that command reordering does not place the instructions behind the memory barrier and does not queue the preceding instruction behind the memory barrier, i.e., the operation in front of the memory barrier is fully completed when the command is executed;
    2. It forces the modification of the cache to be immediately written to main memory;
    3. If it is a write operation, it causes the corresponding cache line in the other CPU to be invalid.

application Scenarios for volatile
Interested can look at the Java Theory and Practice: The correct use of Volatile variables , here is not more introduced.
Later in the introduction of Concurrenthashmap, will understand the role of volatile in the inside.

Reference:
Java concurrency Programming: volatile keyword parsing
Meaning of the volatile keyword in Java
Deep understanding of the Java memory Model (iv)--volatile
Talk about concurrency (i) in-depth analysis of the implementation principle of volatile
Concurrenthashmap

Java basics of Java perplexed-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.