Volatile keywords in Java
Learning Imageloader source encountered such a keyword encountered, did not encounter, and do not understand what the meaning of the Internet to find some information, summary record.
In Java thread concurrency processing, there is a very big confusion about the use of a keyword volatile, for the use of this keyword, in the multi-threaded concurrent processing can be all right. The Java language is multi-threaded, and in order to solve the problem of thread concurrency, the synchronization block and the volatile keyword mechanism are introduced inside the language.
synchronized
Synchronization Block Everyone is more familiar with the Synchronized keyword to achieve, all plus synchronized and block statements, in multi-threaded access, at the same time only one thread can be used synchronized a modified method or block of code.
Volatile
With volatile modifiers, the thread will read the variable's most-modified value each time it uses the variable. Volatile is easily misused and used for atomic operations.
Analyze volatile keywords
1. Two-tier semantics of the volatile keyword
Once a shared variable (a member variable of a class, a static member variable of a class) is modified by volatile, then there are two layers of semantics:
1) ensures the visibility of different threads operating on this variable, that is, one thread modifies the value of a variable, which is immediately visible to other threads.
2) command reordering is prohibited.
Look at a piece of code first, if thread 1 executes first, thread 2 executes:
Thread 1boolean stop = False;while (!stop) { dosomething ();} Thread 2stop = true;
This code is a typical piece of code, and many people may use this notation when they break a thread. But in fact, is this code going to work exactly right? Is it bound to break the thread? Not necessarily, perhaps most of the time, this code can break the thread, but it can also lead to the inability to break threads (although this is a very small possibility, but as soon as this happens it will cause a dead loop).
The following explains why this code may cause the thread to fail. As explained earlier, each thread has its own working memory as it runs, and when thread 1 runs, it copies a copy of the value of the stop variable into its working memory.
Then when thread 2 changed the value of the stop variable, but before it could write to the main memory, thread 2 went to do something else, and thread 1 would have been looping because it did not know that thread 2 had changed the stop variable.
But it becomes different after the volatile modification:
First: using the volatile keyword forces the modified value to be immediately written to main memory;
Second: with the volatile keyword, when thread 2 is modified, it causes thread 1 to work in-memory cache variable stop cache row is invalid (reflected to the hardware layer, the CPU L1 or the corresponding cache line in the L2 cache is invalid);
Third: because thread 1 works in-memory cache variable stop cache line is invalid, so thread 1 reads the value of the variable stop again to read the main memory.
Then thread 2 when modifying the stop value (of course, this includes 2 operations, modify the value in the thread 2 working memory, and then write the modified value to memory), will make thread 1 in the work in-memory cache variable stop cache row is invalid, and then threads 1 read, found that their cache row is invalid, It waits for the cache line corresponding to the main memory address to be updated, and then goes to the corresponding main memory to read the latest value.
Then thread 1 reads the latest correct value.
2. Does volatile guarantee atomicity?
Knowing the volatile keyword above guarantees the visibility of the operation, but does volatile guarantee that the operation of the variable is atomic?
Let's look at an example:
public class Test {public volatile int inc = 0; public void Increase () { inc++; } public static void Main (string[] args) { final Test test = new test (); for (int i=0;i<10;i++) { new Thread () {public void run () {for (int j=0;j<1000;j++) Test.increase (); }; }. Start (); } while (Thread.activecount () >1) //Ensure that the previous thread finishes executing thread.yield (); System.out.println (test.inc); }}
How much do you think the output of this program is? Maybe some friends think it's 10000. But in fact running it will find that each run results in an inconsistent, a number less than 10000.
Maybe some friends will have doubts, no Ah, the above is the variable Inc self-increment operation, because volatile guarantees the visibility, then in each thread after the INC self-increment, in other threads can see the modified value ah, so there are 10 threads have 1000 operations respectively, Then the value of the final Inc should be 1000*10=10000.
There is a misunderstanding here, the volatile keyword can ensure that the visibility is not wrong, but the above procedure is not able to guarantee the atomic nature. Visibility only guarantees the most recent value per read, but volatile has no way of guaranteeing the atomicity of the operation of the variable.
As mentioned earlier, the self-increment operation is not atomic, it includes reading the original value of the variable, adding 1 operations, writing to the working memory. That is, the three sub-operations of the self-increment operation may be split and executed, which may cause the following to occur:
If the value of the variable Inc is 10 at a time,
Thread 1 self-increment the variable, thread 1 reads the original value of the variable Inc first, and then thread 1 is blocked;
Thread 2 then self-increment the variable, thread 2 also reads the original value of the variable inc, because thread 1 only reads from the variable Inc, and does not modify the variable, so it does not cause the cache line of the working in-memory cache variable INC to be invalid for thread 2. So thread 2 will go directly to main memory to read the value of INC, find the value of INC 10, and then add 1 operations, and 11 write to the working memory, and finally write to main storage.
Then thread 1 then adds 1, since the value of the INC has been read, note that at this point the value of the in-Memory inc of Thread 1 is still 10, so that Threads 1 to Inc 1, and then 11 to the working memory, and finally to main storage.
Then, after two threads had a single self-increment operation, Inc increased by 1.
Explained here, there may be friends have doubts, no ah, the front is not to ensure that a variable when modifying volatile variables, will the cache row invalid? Then the other threads read the new value, yes, that's right. This is the volatile variable rule in the Happens-before rule above, but note that after thread 1 reads the variable, it is blocked, and the INC value is not modified. Then, although volatile ensures that the value read of the thread 2 pair of variable Inc is read from memory, thread 1 is not modified, so thread 2 does not see the modified value at all.
The source is here, the self-increment operation is not atomic, and volatile does not guarantee that any operation on the variable is atomic.
Change the above code to any of the following to achieve the effect:
Using synchronized:
public class Test { public int inc = 0; Public synchronized void Increase () { inc++; } public static void Main (string[] args) { final Test test = new test (); for (int i=0;i<10;i++) { new Thread () {public void run () {for (int j=0;j<1000;j++) Test.increase (); }; }. Start (); } while (Thread.activecount () >1) //Ensure that the previous thread finishes executing thread.yield (); System.out.println (test.inc); }}
Using Lock:
Public classTest { Public intinc = 0; Lock lock =NewReentrantlock (); Public voidIncrease () {lock.lock ();Try{inc++; }finally{Lock.unlock (); } } Public Static voidMain (string[] args) {FinalTest test =NewTest (); for(inti=0;i<10;i++) {NewThread () { Public voidRun () { for(intj=0;j<1000;j++) test.increase (); }; }.start (); } while(Thread.activecount () >1)//ensure that the previous threads are finished executingThread.yield (); System.out.println (TEST.INC); }}
Using Atomicinteger:
Public classTest { PublicAtomicinteger inc =NewAtomicinteger (); Public voidIncrease () {inc.getandincrement (); } Public Static voidMain (string[] args) {FinalTest test =NewTest (); for(inti=0;i<10;i++) {NewThread () { Public voidRun () { for(intj=0;j<1000;j++) test.increase (); }; }.start (); } while(Thread.activecount () >1)//ensure that the previous threads are finished executingThread.yield (); System.out.println (TEST.INC); }}
Some atomic operation classes are provided under the Java 1.5 java.util.concurrent.atomic package, namely, the self-increment of the base data type (plus 1 operations), the decrement (minus 1 operations), and the addition operation (plus one number), the subtraction operation (minus one number) is encapsulated, Ensure that these operations are atomic operations. Atomic is the use of CAs to achieve atomic operations (Compare and Swap), the CAs are actually implemented using the CMPXCHG instructions provided by the processor, and the processor execution CMPXCHG instruction is an atomic operation.
Can 3.volatile guarantee order?
It is mentioned that the volatile keyword can prohibit order reordering, so volatile can be guaranteed to a certain degree of order.
The volatile keyword prohibit command reordering has two layers of meaning:
1) When the program performs a read or write operation to a volatile variable, the changes in its preceding operation must have been made, and the result is already visible to the subsequent operation;
2) in the case of instruction optimization, you cannot put the statements that are accessed by volatile variables behind them, and you cannot put the statements that follow the volatile variable in front of them.
Maybe it's more about the above, for a simple example:
X, Y is non-volatile variable//flag is the volatile variable x = 2; Statement 1y = 0; Statement 2flag = true; Statement 3x = 4; Statement 4y =-1; Statement 5
because the flag variable is a volatile variable, then in the process of order reordering, the statement 3 will not be placed in statement 1, Statement 2 before the statement 3 is not put into statement 4, statement 5 after. Note, however, that the order of statement 1 and Statement 2, Statement 4, and statement 5 are not guaranteed.
And the volatile keyword guarantees that execution to the statement 3 o'clock, Statement 1 and statement 2 must be completed, and statement 1 and statement 2 execution results to statement 3, Statement 4, Statement 5 is visible.
So let's go back to one of the examples above:
Thread 1:context = Loadcontext (); Statement 1inited = true; Statement 2 //thread 2:while (!inited) { sleep ()}dosomethingwithconfig (context);
In the preceding example, it is mentioned that it is possible that statement 2 will be executed before statement 1, so long may cause the context to not be initialized, and thread 2 will use the uninitialized context to operate, resulting in a program error.
If the inited variable is decorated with the volatile keyword, this problem does not occur because the context is guaranteed to be initialized when executing to statement 2 o'clock.
Principle and implementation mechanism of 4.volatile
Some of the uses of the volatile keyword are described earlier, so let's look at how volatile guarantees visibility and suppresses command 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 the 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 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 other CPUs to be invalid.
Scenarios using the volatile keyword
The Synchronized keyword is to prevent multiple threads from executing a piece of code at the same time, which can affect program execution efficiency, while the volatile keyword has better performance than synchronized in some cases. Note, however, that the volatile keyword cannot replace the synchronized keyword because the volatile keyword does not guarantee the atomicity of the operation. In general, the following 2 conditions are required to use volatile:
1) The write operation to the variable does not depend on the current value
2) The variable is not included in the invariant with other variables
In fact, these conditions indicate that these valid values that can be written to a volatile variable are independent of the state of any program, including the current state of the variable.
In fact, my understanding is that the above 2 conditions need to ensure that the operation is atomic, so that programs that use the volatile keyword can execute correctly when concurrency occurs.
Here are a few scenarios for using volatile in Java.
1. Status Mark Amount
Volatile Boolean flag = false; while (!flag) { dosomething ();} public void Setflag () { flag = true;}
Volatile Boolean inited = false;//thread 1:context = Loadcontext (); Inited = true; Thread 2:while (!inited) {sleep ()}dosomethingwithconfig (context);
2.double Check
Class singleton{ Private volatile static Singleton instance = null; Private Singleton () { } public static Singleton getinstance () { if (instance==null) { synchronized ( Singleton.class) { if (instance==null) instance = new Singleton (); } } return instance;} }
Reference Links:
http://sakyone.iteye.com/blog/668091
Http://www.cnblogs.com/dolphin0520/p/3920373.html
Volatile keywords in Java