is the 1,volatile keyword guaranteed to be thread safe? ()
Answer: No
The volatile keyword is used in multi-threaded synchronization to guarantee read visibility, and the JVM simply guarantees that the value that is loaded from main memory to the working memory of the thread is the most recent read value, not the cache. However, multiple threads write to volatile, which is not guaranteed to be thread safe.
If thread 1, thread 2 in the read,load operation, found that the value of count in main memory is 5, then the latest value will be loaded, after thread 1 modifies count, write to main memory, the count variable in main memory will become 6 Thread 2 because of the read,load operation, after the operation, the main memory will also be updated count of the variable value of 6, resulting in two threads timely volatile keyword modification, there is still a concurrency situation.
Let's get a good look at the volatile keyword Emmmmmmmmm
The sum up is that volatile does not guarantee thread safety, does not guarantee atomicity, and can guarantee order in some degree
Two-tier semantics for 1.volatile keywords
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 1 Boolean false ; while (! stop) { // thread 2true;
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 causes a dead loop.)
Since each thread has its own working memory during the run, thread 1, when running, 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 : Using the volatile keyword, when thread 2 is modified, 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, thread 1 reads the value of the variable stop again when it reads from 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.
Does 2.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? The answer is no guarantee.
Let's look at an example:
Public classTest { Public volatile intinc = 0; Public voidIncrease () {Inc++; } 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); }}
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, then thread 1 is blocked;
? Then thread 2 self-increment the variable, thread 2 also reads the original value of the variable inc, because thread 1 only reads 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 of thread 2 to be invalid. 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 followed by the plus 1 operation, since the value of the INC has been read, note that at this time the value of the in-Memory inc of Thread 1 is still 10, so threads 1 to the INC 1 operation, the value of INC is 11, then 11 is written 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 classTest { Public intinc = 0; Public synchronized voidIncrease () {Inc++; } 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 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 are non-volatile variables // flag is a volatile variable = 2; // Statement 1y = 0; // Statement 2 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.
// Thread 1:context = Loadcontext (); // Statement 1 true; // // 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.
The following is a scenario that uses the volatile keyword EMMMMMM
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.
1. Status Mark Amount
volatile Boolean false while (! Flag) { publicvoid Setflag () { true;}
volatile Boolean false ; // Thread 1:context = loadcontext (); true ; // Thread 2: while (! inited) {Sleep ()}dosomethingwithconfig (context);
2.double Check
classsingleton{Private volatile StaticSingleton instance =NULL; PrivateSingleton () {} Public StaticSingleton getinstance () {if(instance==NULL) { synchronized(Singleton.class) { if(instance==NULL) Instance=NewSingleton (); } } returninstance; }}
The above is referenced from:java concurrency Programming: volatile keyword parsing
[Analysis of Java]--volatile in winter bamboo Science