Java Multithreading Mechanism series: have to mention the volatile and order reordering (Happen-before)

Source: Internet
Author: User
Tags visibility volatile java se

One, have to mention the volatile

Volatile is a very old keyword, almost with the birth of the JDK, we all know the keyword, but it is not clear when it will be used, we are ubiquitous in the JDK and open source framework, but the concurrency experts often advise us to stay away from it. such as thread, a very basic class, which is very important thread state field, is modified with volatile, see Code

/* Java thread status for tools,
     * Initialized to indicate thread ' not yet started '
     */
    int threadstatus = 0;

As mentioned above, the concurrency expert advises us to stay away from it, especially after the performance of JDK6 's synchronized keyword has been greatly optimized, but it is still a very important thing to look at, but it's a key word to study, and it's not about using it. It is very helpful to understand the whole multithreading mechanism of java.

1. Example

Start by experiencing the effects of volatile, starting with the code below

   1:  class Volatileexample extends thread{
   2:      //Set class static variables, each thread accesses the same shared variable
   3:      false;
   4:      
   5:      //infinite loop, wait for flag to become true before jumping out of the loop
   6:      void Run () {while (!flag) {};} 
   7:      
   8:      void Main (string[] args) throws Exception {
   9:          new Volatileexample (). Start ();
  //sleep:          The purpose of this is to wait for the thread to start, which means to enter the infinite loop of run.
  One:          thread.sleep (100);
  :          true;
  :      }
  :  }

This example is well understood, the main function starts a thread, and its run method is an infinite loop with flags as the flag bit. If flag is true, jumps out of the loop. When main executes to 12 rows, flag is set to true, logically parsing the thread to the end, that is, the entire program is finished executing.

Take a look at what's the result? The result is surprising, and the program will never end. Main is definitely over, because the thread's Run method is not finished, that is, the flag in the Run method is still false.

Add the volatile modifier to line 3rd, which is

False

Take a look again? The result is that the program exits normally, and volatile is in effect.

Let's change it a little bit. Remove the volatile keyword, revert to the starting example, and then change the while (!flag) {} to the while (!flag) {System.out.println (1);}, and then take a look. According to the analysis, no volatile keyword, the program will not execute the end, although the addition of a print statement, but did not make any keyword/logic changes, it should not be the end of the program, but the result is: The normal end of the program.

With these perceptual knowledge, we will analyze the semantics of volatile and its role.

2.volatile semantics

The first semantics of volatile is to ensure the visibility of variables between threads, simply to say that when thread a modifies the variable x, other threads executing behind the thread A can see the change in the variable x, in more detail to conform to the following two rules:

    • After the thread modifies the variable, it writes back to the main memory immediately.
    • When a thread reads a variable, it is read from the main memory, not the cache.

To explain the problem in detail, you have to mention the Java memory Model (JMM). Java memory model is a more complex topic, belongs to the Java Language Specification category, personal level is limited, can not be fully explained in a limited length of this matter, if you want to understand clearly, learn the deep understanding of Java Virtual Machine-JVM advanced features and best practices and the Java Language specification, Java SE 7 Edition, here simply cite some data to explain it slightly.

Java needs to define its own memory model in order to maintain its platform and isolate Java applications from the operating system memory model. In the Java memory model, memory is divided into two parts of primary memory and working memory, where the main memory is shared by all threads, while working memory is allocated for each thread, the working memory of each thread is independent and invisible to each other, and the virtual machine allocates a piece of working memory for each memory when the threads are started. Includes not only local variables defined within threads, but also copies of shared variables (objects constructed in non-threaded constructs) that the thread needs to use, that is, to improve execution efficiency, reading a copy is faster than directly reading main memory (here you can simply understand the main memory as a heap in the virtual machine, and the working memory is understood as a stack ( or virtual machine stack), the stack is a continuous small space, sequential into the stack out of the stack, and the heap is a discontinuous large space, so in the stack to address the speed than the heap much faster. The data exchange between the working memory and the main memory is done through main memory, such as:

At the same time, the Java memory model defines a series of rules for the sequence of interactions between the working memory and the main memory (which is more complex and more complicated, see the "deep understanding of Java Virtual Machine-JVM advanced features and Best practices", chapter 12th 12.3.2), This is only about volatile parts. For shared common variables, when the variable is changed in working memory, it must be written back to working memory (sooner or later but not immediately), but for volatile variables it is necessary to write back to the working memory immediately after the change in working memory. When a thread reads a volatile variable, it must go to the working memory to fetch the latest value instead of reading a copy of the local working memory, which guarantees that "when thread a modifies the variable x, the other threads executing after threads a can see the change in the variable X".

most of the articles on the Internet for the volatile explanation is over, but I think there is still missing, put forward to discuss. Working memory can be said to be a cache of main memory, so volatile needs to discard this cache in order to avoid the inconsistency of the cache. But in addition to the memory cache, at the CPU hardware level there is also a cache, that is, registers. If thread a changes the variable x from 0 to 1, the CPU is operating in its cache and does not write back to the memory in time, then the JVM cannot be x=1 to be seen by thread B that is executed in time, so I think the JVM is handling volatile variables. Also uses the hardware-level cache consistency principle (CPU cache consistency principle See theJava Multithreading Mechanism series: (ii) cache consistency and CAs).

Second semantics of volatile: Prohibit command reordering. For instructions reordering, see the "Order Reordering" section later. This is a major usage scenario for volatile at the moment.

3. Volatile does not guarantee atomicity

The introduction of volatile can not guarantee the atom of the article more, here do not give a detailed example, we can go online to access the relevant information. In the multi-threaded concurrent execution i++ operation results, I plus and no volatile are the same, as long as the number of threads enough, there will be inconsistencies. Here's why it's not guaranteed that the atomic principle is said.

The above mentioned two semantics of volatile ensure the timely visibility of shared variables between threads, but the whole process is not guaranteed to be synchronous (see the Java Multithreading Mechanism series: (a) general and basic concepts describing the two characteristics of "lock"), which is related to the mission of volatile The background of creating it is that in some cases it can replace the synchronized to achieve the purpose of visibility, and avoid the overhead of thread suspending and scheduling caused by synchronized. If the volatile can also guarantee synchronization, then it is a lock, can completely replace the synchronized. From this point of view, volatile can not guarantee synchronization, and is based on the above reasons, as synchronized performance gradually improved, volatile gradually exited the historical stage.

Why is volatile not guaranteed atomicity? Take i++ as an example, which includes reading, manipulating, assigning three operations, the following is the sequence of operations of two threads

If thread A is doing a i+1, but when the value is not assigned, thread B begins to read I, then when thread a assigns a value of I=1 and writes back to the main memory, and then thread B no longer needs the value of I, but instead directly to the processor to do +1 of the operation, so when thread B executes and writes back to main memory, I value is still 1, Rather than the expected 2. That is, volatile shortens the time difference between common variables that are executed between different threads, but there are still loopholes that do not guarantee atomicity.

It is important to mention that, at the beginning of this chapter, "The working memory of each thread is independent and invisible, and the virtual machine allocates a piece of working memory for each memory at the start of the thread, including not only the local variables defined inside the threads, but also a copy of the shared variables (non-thread constructed objects) that the thread needs to use. , that is, to improve execution efficiency "is not accurate. Today's volatile example is difficult to reproduce, such as the beginning of this article only in the while dead loop only to reflect the role of volatile, even if only add System.out.println (1) Such a small segment, ordinary variables can also achieve volatile effect, What is the reason for this? Only when the variable read frequency is very high, the virtual machine will not write back to the main memory in time, and when the frequency does not reach the high frequency that the virtual machine thinks, the common variable and volatile are the same processing logic. If the execution of SYSTEM.OUT.PRINTLN (1) in each loop increases the time interval for reading variables, making the virtual machine think that the read frequency is not so high, so that the effect of achieving and volatile (the example at the beginning of this article is only tested on HotSpot24, Not tested on other versions of the JDK like JRockit). The effect of volatile is easy to reproduce in jdk1.2 and before, but with the continuous optimization of the virtual machine, the visibility of the common variables is not so serious anymore, which is why volatile is not really used today.

4. Application Scenarios of volatile

Concurrency experts suggest that it makes sense to stay away from volatile, and here's a summary:

    • Volatile is raised when the synchronized performance is low. Now the efficiency of synchronized has been greatly improved, so the significance of volatile existence is small.
    • Today's non-volatile shared variables have the same effect as volatile modified variables when access is not super-frequent.
    • Volatile does not guarantee atomicity, this is not very clear, so it is easy to make mistakes.
    • Volatile can prohibit reordering.

So if we are sure that we can use volatile correctly, then it is a good usage scenario when we prohibit reordering, otherwise we don't need to use it again. Here are just a few examples of volatile usage scenarios, that is, when identifying bits (such as the Boolean flag in this example). It is more broadly said that the "write operation on a variable does not depend on the current value and the variable is not contained in the invariant of other specific variables", see Java Theory and Practice: using Volatile variables correctly.

Second, order reordering (Happen-before)

Order reordering is a more complex, feel some incredible problem, the same is the first example (recommended to run the next example, which is actually reproducible, the probability of reordering is very high), there is a perceptual understanding

/**
* A simple example of showing Happen-before.
* There are two shared variables: A and flag, with an initial value of 0 and false. First give a=1 in Threada, then flag=true.
* If ordered, then if the IF (flag) succeeds in threadb, then it should be a=1, and a=a*1 after a is still 1, the lower if (a==0) should never be true and will never print.
* But the actual situation is: In the case of test 100 times there will be 0 or several printing results, and the test 1000 times more obvious results, more than 10 times printing.
*/
Class Simplehappenbefore {
    /** This is a variable of the result of validation */
    int a=0;
    /** This is a flag bit */
    Static Boolean flag=false;
    void Main (string[] args) throws Interruptedexception {
        Because of the multi-threaded situation may not try to reorder the conclusion, so try a few times
        for (int i=0;i<1000;i++) {
            Threada threada=New Threada ();
            THREADB threadb=New threadb ();
            Threada.start ();
            Threadb.start ();
            After waiting for the thread to end, reset the shared variable so that the validation results are easier to work with.
            Threada.join ();
            Threadb.join ();
            a=0;
            Flag=false;
        }
    }
    Class Threada extends thread{
        void Run () {
            A=1;
            Flag=true;
        }
    }
    Class Threadb extends thread{
        void Run () {
            if (flag) {
                a=a*1;
            }
            if (a==0) {
                System. out.println ("ha,a==0"); 
            }
        }
    }
}
The examples are simple and add comments that are not described in detail.
What is Order reordering? There are two levels of:
    • At the virtual machine level, in order to minimize the effect of memory operations much slower than the CPU vacancy caused by CPU running speed, virtual machines break down the sequence of programming in accordance with some of their rules (as described later in this rule)-that is, the code that is written in the following order may be executed first, and written in the previous code will be executed. --to make the best possible use of the CPU. Take the above example: if it is not a=1 operation, but A=new byte[1024*1024] (allocate 1M space), then it will run very slowly, when the CPU is waiting for its execution to end, or first execute the following sentence flag=true it? Obviously, the first implementation of the flag=true can be used in advance of the CPU, speed up overall efficiency, of course, the premise is not to produce errors (after what kind of error). Although there are two cases, the following code begins with the previous code, and the preceding code begins execution, but when the efficiency is slow, the subsequent code starts executing and ends with the previous code execution. No matter who starts first, in short, the following code in some cases there is a possibility to end first.
    • At the hardware level, the CPU will reorder the received batch of instructions according to its rules, which is also based on the reason that the CPU speed is faster than the cache speed, similar to the previous point, except that the hardware processing can only be reordered within the range of limited instructions received at a time, and the virtual machine can be reordered within a larger and more instruction range. Hardware reordering mechanism see CPU memory instruction reordering from JVM concurrency (reordering)

Reordering is very difficult to understand, the above is simply to mention the next scene, to better understand the concept, need to construct some examples and charts, here introduce two more detailed, vivid article "Happens-before Vulgar Solution" and "deep Understanding Java Memory Model (ii)-Re-order". One of the "as-if-serial" should be mastered, namely: no matter how to reorder, the implementation of the single-threaded the results can not be changed. The compiler, runtime, and processor must adhere to the "as-if-serial" semantics. To take a simple example,

void Execute () {
    int a=0;
    int b=1;
    int c=a+b;
}

Here a=0,b=1 two sentences can be arbitrarily ordered, do not affect the program logic results, but c=a+b this sentence must be executed in the back of the first two sentences.

As you can see from the previous example, the probability of reordering in a multithreaded environment is still quite high, there are volatile and synchronized on the keywords can disable reordering, but there are some rules, it is these rules, So that we do not feel the reordering of the bad in the normal programming work.

    • Program Order rule: Within a thread, in code order, the preceding operation precedes the one written in the following operation. It should be accurate to control the flow order rather than the code order, because the structure of branching, looping, etc. is considered.
    • monitor Lock rule: A unlock operation first occurs after a lock operation that faces the same object locks. The emphasis here is on the same lock, while the "back" refers to the chronological order of the time, such as the lock operation that occurs in other threads.
    • volatile variable rules (volatile Variable rule): Writes to a volatile variable occur after the read operation of the variable, where the "back" also refers to the chronological order.
    • thread Start rule: The thread-exclusive start () method precedes every action on this thread.
    • thread termination rule (thread termination rule): Each operation in a thread precedes the termination detection of this thread, and we can end it with the Thread.Join () method, Thread.isalive () The return value detects that the thread has terminated execution.
    • thread Interruption rule: The call to the thread Interrupte () method takes precedence over the code of the interrupted thread to detect the occurrence of the interrupt event, which can be passed through the thread.interrupted () method to detect whether a thread has been interrupted.
    • Object Finalization principle (Finalizer rule): Initialization of an object (the completion of a constructor execution) occurs first at the beginning of its finalize () method.
    • transitivity (transitivity): If operation a precedes operation B, action B first occurs in Operation C, it is concluded that operation a precedes operation C.

It is these rules that guarantee the order of the Happen-before, and if they do not conform to the above rules, there is no guarantee that the execution order is equivalent to the code order in a multithreaded environment, i.e. "if you observe in this thread, all operations are orderly; If you observe another thread in one thread, does not conform to the above rules are unordered, so if our multi-threaded program depends on the code writing order, then we should consider whether to conform to the above rules, if the non-conformance will be through some mechanism to conform to, the most commonly used is synchronized, lock and the volatile modifier.

Transferred from: http://www.cnblogs.com/mengheng/p/3495379.html

Java Multithreading Mechanism series: have to mention the volatile and order reordering (Happen-before)

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.