Overview
In multi-threaded concurrent processing, it is often necessary to make visibility access to resources and mutually exclusive synchronization operations. Sometimes we may learn from our predecessors that we need to do volatile or synchronized keyword retouching on our resources. However, we do not know the difference between the two, we cannot tell when to use which keyword. In this article, we will discuss this issue.
Copyright notice
Copyright belongs to the author.
Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
This article Coding-naga
Published: April 5, 2016
This article link: http://blog.csdn.net/lemon_tree12138/article/details/51062421
Source: CSDN
More content: Classification >> concurrency and multithreading
Introduction to Memory semantic Analysis Happens-before model
If you understand the Happens-before model by its literal meaning, you may think that this is said to be performed before another operation. However, after learning Happens-before, you will not still understand this. The following is the definition of happens-before in the art of Java concurrent programming:
In JMM (Java Memory Model), if the result of one operation needs to be visible to another operation, there must be a happens-before relationship between the two operations. The two actions mentioned here can be either within a thread or between different threads.
Volatile memory semantics
For multithreaded programming, each thread can have a copy of a variable in shared memory, which is still covered later, and is not explained much here. If a variable is modified by the volatile keyword, then the write of the variable in the local memory is flushed to the shared memory, and the reading of the variable is somewhat different, ignoring the copy of his local memory while reading, just reading the data from the shared variable.
Memory semantics of synchronized
We say that synchronized is actually locking the variable. So either read or write is based on the lock operation of this variable. If a variable is modified by the Synchronized keyword, the write to that variable is to flush the local in-memory copy to the shared memory, and the read of the variable is to flush the value in the shared memory to the local memory and read the data from the local memory. Because the variables are locked during the whole process, other threads cannot read and write to the variable. So it can be understood that any operation on this variable is atomic, that is, the thread is safe.
Example demonstration
Some of the above explanations or definitions may be tedious and boring, and not very well understood. Here we cite some examples to illustrate, so more specific and image some.
Volatile visibility test
Runthread.java
Public class runthread extends Thread { Private BooleanIsRunning =true; Public Boolean isrunning() {returnisrunning; } Public void Setrunflag(BooleanFlag) {isrunning = flag; }@Override Public void Run() {System.out.println ("I ' m come in ...");BooleanFirst =true; while(isrunning) {if(first) {System.out.println ("I ' m in while ..."); First =false; }} System.out.println ("I ll go out."); }}
Myrun.java
public class myrun { public static void main (string[] args) throws interruptedexception {RunTh Read thread = new runthread (); Thread.Start (); Thread.Sleep (100 ); Thread.setrunflag (false ); System.out.println ( "flag is reseted:" + thread.isrunning ()); }}
For the above example is just a very common multithreaded operation, here we can easily get the runthread thread in the while into the dead loop.
We can see a thread.sleep in the main () method, combined with the Happens-before memory model mentioned earlier, the following Thread.setrunflag (false) Does not happens-before the while in the child thread. In this way, while the isrunning is modified in the main thread, the while in the child thread does not change, so this throws a dead loop in the while.
In this case, the memory model that the thread is working on is like this
Here, you might wonder why there are two "blocks of memory"? This is due to multi-threaded performance considerations. Although the memory allocated by the object and the member variable is in shared memory, it is possible for each thread to have a copy of the object, which is intended to speed up the execution of the program, which is a notable feature of modern multicore processors. As you can see from the memory model above, the Java thread interacts directly with its own working memory (local memory) and the working memory interacts with the shared memory. This creates a non-atomic operation that is dangerous for non-atomic operations in a multithreaded environment in Java. We already know this because it can be broken by asynchronous read and write operations.
Here the working memory is occupied by while, unable to update the main thread to the shared memory isrunning variable modification. So, if we want to break this limit, we can deal with the volatile keyword. The conditional variable of the while is decorated with the volatile keyword, which is isrunning. Modify the Runthread.java code as follows:
privatevolatilebooleantrue;
In this way, volatile modifies the visibility of the isrunning so that the Thread.setrunflag (false) of the main thread happens-before the while in the child thread. Finally, the child thread jumps out of the while loop and the problem is resolved.
Let's look at how volatile changes the visibility of isrunning.
Here, because IsRunning is modified by volatile, when a child thread wants to access the inrunning in working memory, it is forcibly fetched directly from the shared memory. While the isrunning in shared memory has been modified by the main thread, it has been modified to false and the while is broken, so the thread jumps out of the while loop.
Volatile atomicity Test
Volatile does have a number of advantages, but it has a fatal disadvantage, which is that volatile is not an atomic operation. In the case of multithreading, it is still unsafe.
Perhaps, this time you will ask, since volatile guarantees its visibility between threads, then when to modify it, how to modify it, for the other thread is visible, a thread will read a modified value, why still say it is not safe?
Let's go through an example to illustrate it, so it's more figurative. Let's look at the following piece of code:
Public class demonoprotected { StaticClass MyThread extends Thread {Static intCount =0;Private Static void Addcount() { for(inti =0; I < -; i++) {count++; } System.out.println ("Count ="+ count); }@Override Public void Run() {addcount (); } } Public Static void Main(string[] args) {mythread[] threads =Newmythread[ -]; for(inti =0; I < -; i++) {Threads[i] =NewMyThread (); } for(inti =0; I < -; i++) {Threads[i].start (); } }}
300300300400......761875189918
This is a straightforward process without any processing. But the result, too, is straightforward. In fact, the results are not surprising, from the time we learn Java, we know that Java multithreading is not secure. is not from the above study, you feel this can be solved by the volatile keyword? Since you say so, let's try it and add the volatile keyword to the count variable, as follows:
public class demovolatile { static class MyThread extends Thread {static volatile int count = 0 ; ... ... } public static void main (string[] args) { ... ... }}
100300400200......985297529652......81548054
I don't know if this result will make you feel an accident. The confusing number of count is a good understanding, and it should happen when multiple threads are modified at the same time. But we can't find a logical maximum of "10000" in the results, which is a bit odd. Because, logically, volatile modifies the visibility of count, it is a modification of the visible thread B to count for thread A. This is not reflected in the results.
We say that volatile does not guarantee thread safety. In the Addcount () method in the above sub-thread, the code is executed in count++. And a code like count++ from the first lesson of learning Java variables, the teacher should emphasize the process of its execution. count++ can be likened to the following process:
int1;count = tmp;
Visible, count++ is not an atomic operation. Any two threads are likely to separate the above code, and security is out of the way.
So, here we know that volatile can change the visibility between variables on the thread, but it doesn't change the synchronization between threads. Synchronous operations require additional operations to ensure that they are not.
Synchronized synchronization test
It says that volatile does not solve the thread's security problem because volatile cannot build atomic operations. And in multithreaded programming has a very convenient synchronous processing, is the Synchronized keyword. Let's look at how synchronized handles multi-threaded synchronization, the code is as follows:
Public class demosynchronized { StaticClass MyThread extends Thread {Static intCount =0;Private synchronized Static void Addcount() { for(inti =0; I < -; i++) {count++; } System.out.println ("Count ="+ count); }@Override Public void Run() {addcount (); } } Public Static void Main(string[] args) {mythread[] threads =Newmythread[ -]; for(inti =0; I < -; i++) {Threads[i] =NewMyThread (); } for(inti =0; I < -; i++) {Threads[i].start (); } }}
100200300......9800990010000
By synchronized we can easily get the desired results. The memory model of the Synchronized keyword can be represented in this way:
When a thread accesses a variable that is synchronized modified, it locks the shared memory of the variable, and then the access of other threads to it is mutually exclusive. Synchronized's internal implementation is also a lock concept.
Ref
- The core technology of Java multithreaded programming
- The art of Java concurrent programming
Comparison of synchronized and volatile in Java multi-thread