Java's volatile keyword first occurrence of the principles and Detailed

The volatile keyword can be said to be the most lightweight synchronization mechanism provided by the Java virtual machine, but it is not easy to completely correct and complete understanding that many programmers are accustomed not to use it, met to deal with multiple threads data competition issues when using all synchronized for synchronization. Understand the semantics of volatile variables to other characteristics of meaningful understanding of multi-threaded operation, in this article we will explain what volatile semantics in the end yes. Due to the volatile keyword in Java memory model (Java Memory Model, JMM) have more relevance, and therefore before the introduction of the volatile keyword we will first introduce Java memory model.

Java memory model, please refer to the Java Memory Model

 

The 8 memory access operations and limited the above rules, plus some special provisions for volatile later introduced, it has been completely determined which memory access operations in concurrent Java programs are safe. Since this is defined very strict but very cumbersome, practice a lot of trouble. Therefore, the following describes the definition of a judgment principle equivalent - occurs first principle for determining a concurrent access environment is safe.

1. The first principle occur

Java language has a principle of "first occurrence" (happens-before) a. This principle is very important, which is to determine whether there is competition data, based primarily on whether the thread safe, relying on this principle, we can solve all the problems the possibility of conflict exists between two concurrent operating environment by several rules.

Now take a look at the principle of "first occurrence" refers to what it is. Occurs first partial order relation between two operations defined in the Java memory model, if operation occurs in the first A operation B, in fact, that before the occurrence of operation B, the operation can affect the operation of the A B produced was observed, "Effect" includes modifying the values ​​of shared variables in memory, send a message, calling the method. This sentence is not difficult to understand, but what does it mean? We can give an example to explain this three pseudo code as shown in the code.

// The following operations are performed in the thread A, 
K =. 1 ;
 // The following operations are performed in the thread B 
J = K;
 // The following operations are performed in the thread C, 
k = 2;

A thread is assumed that the operation based on the "k = 1" occurs in advance of the operation of the thread B "j = k", it can be determined after thread B executed operation, the value of the variable j is equal to a constant, this conclusion has two: First, the principle of the results occurs first, "k = 1" can be observed; two thread C is not "stage", no other thread will modify the value of the variable k after the thread a operation. Let us now consider the thread C, we still maintain a relationship between the first thread A and thread B, and C appear in the thread between the operating thread A and thread B, and thread B and C but the thread does not matter first occurred, it's j The value is how much? The answer is uncertain! 1 and 2 are likely, because of the impact of the variable k Thread C may be observed that thread B, or may not, at this time there is a risk the thread B to read stale data, do not have multi-thread safe.

 

Here are some "natural" relationship first occurred under the Java Memory Model, these advance a relationship without any assistance synchronizer already exists, you can use directly in the code. If the relationship between the two operations is not listed, and can not be derived from the following rules, then they have no order of protection, a virtual machine can reorder them freely.

  • Rule program sequence (Program Order Rule): in a thread , according to a program order of the code book EDITORIAL write operation occurs after the first operation. Rather, it should be the control flow sequence order rather than program code, to be considered as branches, loops, and other structures.
  • Tube-lock rules (Monitor Lock Rule): a unlock operation ahead with a lock of lock operation in the face after the occurrence. Here it must be emphasized that the same lock, and "back" refers to the order in time.
  • volatile variable rules (Volatile Variable Rule): write to a volatile variable first occurred in the face of this variable after the read operation, where "behind" also refers to the order in time.
  • Thread start rule (Thread Start Rule): start Thread object () method first occurrence in this thread every movement.
  • Thread terminates rules (Thread Termination Rule): All operations are ahead of thread termination occurs in the detection of this thread, we can end by Thread.join () method, Thread.isAlive () return value and other means has detected a thread terminated.
  • Thread break the rules (Thread Interruption Rule): calling thread interrupt () method first occurred in the interrupted thread code detection to interrupt event occurs, can be detected by Thread.interrupted () method if an interrupt occurs.
  • End of rule objects (Finalizer Rule): completion of initialization of an object (constructor executes end) first occurred in its finalize () method to start.
  • Transitivity (Transitivity): if the operation occurs ahead A Procedure B, B ahead operation occurs in the procedure C, it can be concluded that the operation A is C ahead operation occurs.

Java language rule synchronization occurs without any advance will be able to set up the means to protect only those above, and below show you how to use these rules to determine whether the inter-operate with the order of, for the operation of reading and writing shared variables, it is whether the thread safety readers can also feel from the example below what is different about the "order of time" and "first occurrence" between.

private int value=0;
pubilc void setValue(int value){ 
    this.value=value;
}
public int getValue(){ 
    return value;
}

The above code is a set of common, but the getter / setter methods, the presence of thread A and B are assumed, thread A first (successively in time) call "setValue (1)", then thread B calls the same object " getValue () ", then the return value of the thread B received what is?

The answer is uncertain.

why?

Next we turn to analyze the principles of the rule occurred in the first, due to the two methods are called by the thread A and thread B, not a thread, so the program sequence rule does not apply here; the absence of sync blocks, not natural lock and unlock operations will occur, so the tube lock rule does not apply; Since the volatile keyword value variable is not modified, so volatile variable rule does not apply; behind the thread starts, terminates, rules and objects termination rules and there is no relationship. Because the first occurrence of a rule does not apply, so the last pass is also out of the question, so we can determine Although A thread on the operating time prior to thread B, but can not determine the thread B, "getValue ()" method returns the result in other words, there's not thread-safe operations.

 

That's how to fix this problem?

We have at least two relatively simple solutions can be selected:

  1. Either the getter / setter methods are defined as synchronized method, so that you can apply the tube locking rules;
  2. Either the value is defined as the volatile variable, due to the modification of the value setter method does not depend on value of the original value, to meet the volatile keyword usage scenarios, so you can apply the rules to achieve variable volatile relationship first occurred.

By the example above, we can conclude that: an action "occurs first in time" does not mean that this operation will be the "first occurrence", and that if an operation "first occurrence" will be able to deduce whether this operation must be "time occurs first on "mean? Unfortunately, this reasoning is not established, a typical example is mentioned several times "instruction reordering", as shown in the following code examples demonstrate

// The following operations are performed in the same thread 
int I =. 1 ;
 int J = 2;

Listing of two assignments occur in the first "int j = 2" in the same thread, according to the program sequence rules, "int i = 1" operation, but "int j = 2" code may well be the first processors, this does not affect the validity of the principle of first happened, because we can not perceive this point in this thread.

Taken together the above two examples to prove a conclusion: the first occurred in chronological order with basically no significant relationship between the principle, so when we measure the concurrency safety issues do not suffer interference in chronological order, everything must be subject to the principle of first occurrence .

 

2. volatile Detailed

2.1. Volatile characteristics

Java memory model for volatile specifically defines some special access rules, when a variable is defined as volatile, it will have two properties.

  1. This variable to ensure the visibility of all threads , that is, when a thread modifies the value of this variable, the new values for the other thread is immediately learned. Ordinary variable can not do this, the value of common variable passed between threads are required to complete the main memory, e.g., a common thread A modification value of the variable, and then written back to main memory, an additional thread in the thread B A back again after the finish has become a read operation from the main memory, the new value of the variable will be visible to thread B.
  2. Prohibit instruction reordering optimization . Common variable will only ensure that all depend on the result of the assignment of places can get during the execution of the method to the correct result, but can not guarantee the consistent implementation of the order and the program code in the variable assignment. Because during the execution of a method thread can not perceive this point, which is the so-called "internal thread performance for serial semantics" Java memory model described in (Within-Thread As-If-

2.2. Volatile can guarantee atomicity it?

It does not guarantee atomicity volatile, leading to volatile variables in the calculation as concurrent unsafe.

Such as: increment operator under multiple threads

public class VolatileTest {
    
    public static volatile int race = 0;
 
    private static final int THREADS_COUNT = 20;
    
    public static void increase() {
        race++;
    }
 
    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[THREADS_COUNT];
        for (int i = 0; i < THREADS_COUNT; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        increase();
                    }
                }
            });
            threads[i].start();
        }
 
        while (Thread.activeCount() > 1) {
            Thread.yield();
        }
        System.out.println(race);
    }
}
View Code

If this code is executed under the IDEA infinite loop, use the DEBUG can run, you can see specific reasons: the interview will ask CAS, you understand?

Examples of analysis:

This code was launched 20 threads, each of the race variables 10,000 increment operator, if the code can correct concurrent, the result of the final output should be 200,000. The results After running this code, and will not achieve the desired, and will find each time you run the program, the resulting output is not the same, is a number less than 200,000, this is why?

The problem arises in the increment operator "race ++" among decompile the code we will find Javap increase of only one line of code () method is a 4-byte code instructions constituted in Class document, from the byte the code level, it is easy to analyze the reasons for the failure of the concurrent: when getstatic command of the value of the race to take the top of the stack operation when, volatile keyword to ensure that the value of the race at this time is correct, but in execution iconst_1, iadd these when instruction, other threads may have increased the value of the race, while the top of the stack in the value of the operation becomes stale data, so after putstatic instruction execution may value the smaller race synchronized back into the main memory .

GetStatic // Get static variables race, and the value onto the stack 
iconst_1   // int value 1 is pushed to stack 
the iadd       // to stack two int type values and the addition result onto the stack 
putstatic // is static variables race assignment

From this example we can determine the volatile can not guarantee atomicity , atomic operations to ensure that some of the atoms can be used in the operation class java.util.concurrent.atomic package. The most common example: AtomicInteger.

 

2.3. Volatile to ensure the orderliness of it?

In the above-mentioned characteristics in volatile volatile keyword can disable command reordering, so volatile to ensure the orderly to a certain extent.

See a blog in the double detection mechanism to achieve a singleton parts.

 

2.4. Volatile usage restrictions

 

Due to volatile variables can only guarantee visibility in the operation scene does not comply with the following two rules, we still have to pass the lock (or synchronized using atomic classes in java.util.concurrent) to ensure atomicity.

Result of the operation does not depend on the current value of the variable, or to ensure that only a single thread modifies the value of the variable.
Variables do not need to participate in invariants with other state variables.

2.5. Volatile usage scenarios

2.5.1. Status flag quantity

To modify the state of the amount of label that states the amount of label for all threads are visible in real time, thus ensuring that all threads can be real-time access to the latest state of the amount of label, to decide whether to further operate using volatile. Such as a common promotional activities "spike" that can be modified by volatile "is sold out" field, in order to ensure concurrency, whether correctly handling merchandise sold.

volatile boolean flag = false;
while(!flag){
    doSomething();
}
public void setFlag() {
    flag = true;
}

2.5.2 Dual detection mechanism implemented singleton

Ordinary double detection mechanism in the extreme case, the instruction reordering problems occur through the use of volatile to modify instance, prohibit instruction reordering, which can correct implementation singleton.

public  class the Singleton {
     // privatization constructor 
    Private the Singleton () { 
    } 
    // volatile modified singleton object 
    Private  static  volatile the Singleton instance = null ; 
 
    // factory methods provided by the external 
    public  static the Singleton the getInstance () {
         IF (instance == null ) { // first detected 
            the synchronized (the Singleton. class ) {     // synchronization lock 
                IF (instance == null ) { // second test 
                    instance =new new the Singleton (); // initialize 
                } 
            } 
        } 
        return instance; 
    } 
}

 

3. Summary:

  1. Each thread has its own working memory, working memory and the data is not refreshed in real time back to main memory, so in the concurrent case, thread A may have modified the value of a member variable k, but can not read and thread B a modification value of the thread, because working memory thread a has not been flushed back to the main memory, causes the thread B can not be read to the latest value.
  2. Must refresh the latest value before the start of the main memory in the working memory, volatile variables each use, which ensures that the current thread can see the modified value other threads to volatile variables have done.
  3. In the working memory, each modification declared volatile variables must be immediately synchronized back to the main memory, which ensures that other threads can see the changes on their own volatile variables have done.
  4. volatile variables are not optimized instructions reordered to ensure the same execution sequence of the program code.
  5. volatile ensure visibility, does not guarantee the atomicity, ordering part to ensure (guarantee only volatile variables).
  6. Instruction reordering purpose is to improve performance, instruction reordering only guaranteed not to change the final result in a single thread of execution, but can not guarantee the results in a multi-threaded.
  7. In order to achieve volatile memory semantics, the compiler when generating bytecode will be inserted in the instruction sequence memory barrier to prohibit reordering.

 

 

reference:

1. the Java Concurrency: volatile Comments

2. the Java Concurrency: volatile keyword analysis (personal feeling is quite clear)

 

Guess you like

Origin www.cnblogs.com/gjmhome/p/11413696.html