Fully understand the Java memory modelSeptember 21, 2016 18:39:21 
 
  
  - Label:
- Memory model/
- Volatile/
- Concurrent Programming/
- Synchronous
The Java memory model , called JMM. JMM defines how the Java Virtual Machine (JVM) works in Computer memory (RAM). The JVM is the entire computer virtual model, so JMM is subordinate to the JVM.
If we want to learn more about Java concurrency programming, we need to understand the Java memory model first. The Java memory model defines the visibility of shared variables between multiple threads and how to synchronize shared variables when needed. The original Java memory model was not very efficient, so the Java1.5 version was refactored, and now the JAVA8 is still in the Java1.5 version.
About concurrent programming
In the area of concurrent programming, there are two key issues: communication and synchronization between threads.
Communication between threads
Thread communication refers to the mechanism by which the threads exchange information. In imperative programming, the communication mechanism between threads has two kinds of shared memory and message passing .
In the concurrency model of shared memory , the common state of the program is shared between threads, and the threads communicate implicitly through the common state in the write-read memory, and the typical shared memory communication is through the shared object .
In the concurrency model of messaging , there is no public state between threads, and threads must communicate explicitly by sending messages explicitly, and in Java the typical message delivery method is wait () and notify ().
For communication between Java threads, refer to the communication between threads (thread signal).
Synchronization between threads
Synchronization refers to the mechanism that a program uses to control the relative order of operations between different threads.
In the shared-memory concurrency model, synchronization is performed explicitly. Programmers must explicitly specify a method or a piece of code that requires mutually exclusive execution between threads.
In the concurrency model of message passing, synchronization is implicit because the message must be sent before the message is received.
Concurrency in Java takes a shared memory model
Communication between Java threads is always implicit, and the entire communication process is completely transparent to the programmer. If a Java programmer writing a multithreaded program does not understand the working mechanism of implicit communication between threads, it is likely to encounter a variety of strange memory visibility issues.
Java memory model
As mentioned above, the communication between Java threads is a shared memory model, and the shared memory model referred to here refers to the Java memory Model (JMM),JMM determines when a thread's write to a shared variable is visible to another thread . From an abstract point of view, JMM defines the abstract relationship between threads and main memory: shared variables between threads are stored in main memory, each thread has a private local memory (local memory), and the thread is stored locally in RAM to read/ Write a copy of the shared variable . Local memory is an abstract concept of JMM and is not really there. It covers caching, write buffers, registers, and other hardware and compiler optimizations.
From the point of view, if you want to communicate between thread A and thread B, you have to go through the following 2 steps:
 
These two steps are described below:
As shown, local memory A and B have a copy of the shared variable x in main memory. Suppose initially, these three in-memory x values are 0. When thread a executes, it temporarily stores the updated X value (assuming a value of 1) in its own local memory a. When thread A and thread B need to communicate, thread a first flushes its local in-memory modified x value to main memory, when the X value in main memory becomes 1. Then, thread B goes to main memory to read the x value of the updated thread A, at which point the X value of the local memory of thread B also becomes 1.
Overall, these two steps are essentially thread a sending a message to thread B, and the communication process must go through main memory. JMM provides a memory visibility guarantee for Java programmers by controlling the interaction between main memory and the local memory of each thread.
It also says that the Java memory model is just an abstract concept, so how does it work in Java specifically? To better understand how the Java memory model works, the following is a detailed description of the JVM's implementation of the Java memory model, the hardware memory model, and the bridging between them.
JVM implementation of the Java memory model
Inside the JVM, the Java memory model divides memory into two parts: the thread stack and the heap, showing a logical view of the Java memory model in the JVM:
Each thread running in the JVM has its own thread stacks, and the thread stack contains information about the method calls that the current thread executes, which we also call the stack. As the code continues to execute, the call stack is constantly changing.
The line stacks also contains all the local variable information for the current method. A thread can only read its own line stacks, which means that local variables in the thread are not visible to other threads. Even if two threads are executing the same piece of code, they will each create local variables in their own line stacks, so local variables in each thread will have their own version.
All native variables of the original type (boolean,byte,short,char,int,long,float,double) are stored directly in the thread stack and are independent of each other for their values. For native variables of the original type, one thread can pass one copy to another thread, when it is not shared between them.
The heap contains all the object information created by the Java application, regardless of which thread the object was created in, including the original type's encapsulated class (such as Byte, Integer, long, and so on). Whether an object belongs to a member variable or a local variable in a method, it is stored in the heap area.
Shows that both the call stack and local variables are stored in the stack area, and the objects are stored in the heap:
A local variable, if it is the original type, is completely stored in the stack area.
A local variable can also be a reference to an object, in which case the local reference is stored in the stack, but the object itself is still stored in the heap.
For a member method of an object, these methods contain local variables that still need to be stored in the stack area, even if they belong to the object in the heap.
For a member variable of an object, regardless of whether it is a primitive type or a wrapper type, it is stored in the heap area.
Variables of the static type and information about the class itself will be stored in the heap as the class itself.
Objects in the heap can be shared by multithreading. If a thread obtains an application for an object, it can access the member variables of the object. If two threads invoke the same method of the same object at the same time, the two threads can access the object's member variables at the same time, but for local variables, each thread copies one copy to its own line stacks.
Shows the process described above:
Hardware Memory Architecture
No matter what the memory model, and ultimately the computer hardware, so we need to understand the computer hardware memory architecture, a simple description of the contemporary computer hardware memory Architecture:
Modern computers typically have more than 2 CPUs, and each CPU may contain more than one core. Therefore, if our application is multithreaded, these threads may run in parallel in each CPU core.
There is a set of CPU registers inside the CPU, which is the CPU's memory. The CPU operation register is much faster than the operating computer main memory. There is also a CPU cache between main memory and CPU registers, and CPU caches are faster than main memory but slower than CPU registers. Some CPUs may have multiple cache tiers (cache level and level two cache). The main memory of the computer is also called RAM, and all CPUs have access to memory, and memory is much larger than the cache and registers mentioned above.
When a CPU needs to access main memory, a portion of the main memory data is read to the CPU cache, which in turn reads the CPU cache to the register. When the CPU needs to write data to main memory, it will flush the registers to the CPU cache first, and then flush the cached data to main memory at some nodes.
Bridging between the Java memory model and the hardware architecture
As mentioned above, the Java memory model is inconsistent with the hardware memory architecture. Hardware memory architecture does not distinguish between stacks and heaps, from the hardware, whether stack or heap, most of the data will be stored in main memory, of course, some stacks and heap data may also be stored in the CPU register, as shown in the Java memory model and the computer hardware memory architecture is a cross-relationship:
When objects and variables are stored in each area of the computer's memory, there are certain problems, the two most important of which are:
1. 共享对象对各个线程的可见性2. 共享对象的竞争现象
 
The visibility of shared objects
When multiple threads operate on the same shared object at the same time, if there is no reasonable use of the volatile and synchronization keywords, the update of one thread to the shared object may cause other threads to be invisible.
Imagine that our shared object is stored in main memory, a thread in a CPU reads the main memory data to the CPU cache, and then makes changes to the shared object, but the changed object in the CPU cache is not flush to main memory, and the changes to the shared object are not visible to the threads in the other CPUs. Ultimately, each thread will eventually copy the shared object, and the copied object is in a different CPU cache.
Shows the process described above. The thread running on the left side of the CPU copies the shared object obj from main memory to its CPU cache, changing the count variable of object obj to 2. However, this change is not visible to threads running on the right side of the CPU because this change has not yet been flush into main memory:
To solve the problem of shared object visibility, we can use the Java volatile keyword. Java ' s volatile keyword. The volatile keyword guarantees that the variable will be read directly from main memory, and the update to the variable will be written directly to memory. The volatile principle is based on CPU memory barrier directives, which are described later.
Competitive phenomenon
If multiple threads share an object, if they modify the shared object at the same time, this creates a competitive phenomenon.
As shown, thread A and thread B share an object, obj. Assuming thread a reads the Obj.count variable from main memory to its own CPU cache, thread B also reads the OBJ.COUNT variable into its CPU cache, and the two threads add 1 to Obj.count. At this point, the Obj.count plus 1 operation was executed two times, but in different CPU caches.
If these two plus 1 operations are executed serially, then the Obj.count variable will add 2 to the original value, and the value of Obj.count in main memory will be 3. However, the two plus 1 operations are parallel, whether it is thread A or thread B first flush the results into main memory, the final main memory of the Obj.count will only increase 1 times to 2, although there are two times plus 1 operations.
To solve the above problem we can use Java synchronized code block. The synchronized code block guarantees that only one thread can enter the code competition area at the same time, and the synchronized block also guarantees that all variables in the code block will be read from main memory, and that when the thread exits the code block, updates to all variables will be flush to main memory. Regardless of whether these variables are volatile type.
Volatile and synchronized differences
See the difference between volatile and synchronized for details.
Basic principle instruction reordering for supporting Java memory models
In order to improve performance, the compiler and processor re-order the instructions when executing the program. However, JMM ensures that a Memory Barrier consistent memory visibility guarantee is provided for the upper layer by inserting specific types to suppress certain types of compiler reordering and handlers on top of different compilers and different processor platforms.
 
 
  
  - Compiler-Optimized reordering: The compiler can reschedule the execution of statements without changing the semantics of the single-threaded procedure.
- Instruction-level parallelism: If you do not store L in data dependencies, the processor can change the order in which the statements correspond to machine instructions.
- Reordering of memory Systems: processors use cache and read-write buffers, which makes loading and storage operations appear likely to be executed in a disorderly sequence.
Data dependencies
If two operations access the same variable, one of which is a write operation, there is a data dependency between the two operations.
The compiler and processor do not change the order in which the two operations exist that have data dependencies, that is, they do not reorder.
As-if-serial
No matter how it is reordered, the execution results of a single thread cannot be changed, and the compiler, runtime, and processor must obey as-if-serial semantics.
Memory Barrier (Barrier)
As mentioned above, the memory barrier allows the reordering of certain types of processors to be suppressed, allowing the program to execute as we expect. The memory barrier, also known as the memory fence, is a CPU instruction, basically it is an instruction like this:
 
 
  
  - guarantees the order in which specific operations are executed.
- Memory visibility that affects some data, or the execution result of an instruction.
The compiler and CPU are able to reorder the instructions, guaranteeing the same results, and trying to optimize performance. Inserting a memory barrier tells the compiler and the CPU that no instruction can be reordered with this memory barrier instruction.
The other thing that Memory barrier does is to force the various CPU caches to be brushed out, such as a Write-Barrier (write barrier) that will flash all the data written to the cache before barrier, so that any thread on the CPU can read the latest version of the data.
What does this have to do with Java? The volatile described in the above Java memory model is implemented based on memory barrier.
If a variable is volatile decorated, JMM inserts an instruction after the field is written Write-Barrier , inserting an instruction before reading the field Read-Barrier . This means that if you write a volatile variable, you can guarantee that:
 
 
  
  - Once a thread has written to variable a, any thread accessing the variable will get the latest value.
- The write operation before the variable A is written, and its updated data is also visible to other threads. Because the memory barrier will brush out all previous writes in the cache.
Happens-before
Starting with Jdk5, Java uses the new JSR-133 memory model, based on the concept of Happens-before, to illustrate the memory visibility between operations.
In JMM, if the result of an operation needs to be visible to another operation, there must be a happens-before relationship between the two operations, with two operations on either the same thread or a different two thread.
The Happens-before rules that are closely related to programmers are as follows:
 
 
  
  - Program Order rules: Each action in a thread is happens-before to any subsequent action in that thread.
- Monitor lock rule: the unlocking operation of a lock is happens-before to the subsequent lock operation of the lock.
- Volatile domain rule: writes to a volatile domain, happens-before on any thread for subsequent reads of this volatile field.
- Transitive rules: If a happens-before B, and B happens-before C, then a Happens-before c.
Note: There is a happens-before relationship between the two operations, which does not mean that the previous operation must be performed before the next operation! Only the result of the execution of the previous operation is required, which is visible for the latter operation, and the previous operation is sorted in order before the next operation.
Reference Documentation:
1. Http://www.infoq.com/cn/articles/java-memory-model-1
2. Http://www.jianshu.com/p/d3fda02d4cae
Fully understand the Java memory model