The following is transferred from http://ifeve.com/java-memory-model-6/:
The Java memory model regulates how the Java virtual machine and the computer's memory work together. The Java Virtual machine is a model of a complete computer, so the model naturally contains a memory model-also known as the Java memory model.
If you want to design a well-behaved concurrency program, it is important to understand the Java memory model. The Java memory model specifies how and when you can see the values of shared variables that were modified by other threads, and how to synchronize access to shared variables if necessary.
There are some deficiencies in the original Java memory model, so the Java memory model is revised Java1.5. This version of the Java memory model is still in use in Java8.
Internal principles of the Java memory model
The Java memory model divides the inside of a Java virtual machine into lines stacks and heaps. This diagram illustrates the logical view of the Java memory model.
Each thread running in a Java Virtual machine has its own line stacks. This thread stack contains information about the current execution point of the method called by this thread. A thread can only access its own line stacks. A local variable created by one thread is invisible to other threads and is visible only to itself. Even if two threads execute the same code, these two lines Cheng Jinghan the code in their own line stacks to create local variables. Therefore, each thread has a unique version of each local variable.
All native variables of the original type are stored on the thread stack and are therefore not visible to other threads. One thread may pass a copy of a primitive type variable to another thread, but it cannot share the original type variable itself.
The heap contains all the objects created in the Java program, regardless of which object was created. This includes the object version of the original type. If an object is created and assigned to a local variable, or is used as a member variable for another object, the object is still stored on the heap.
The following illustration shows the call stack and local variables are stored on the thread stack, and the objects are stored on the heap.
A local variable might be the original type, in which case it always "stays" on the thread stack.
A local variable may also be a reference to an object. In this case, the reference (the local variable) is stored on the thread stack, but the object itself is stored on the heap.
An object may contain methods that may contain local variables. These local variables are stored on the thread stack, even if the objects they belong to are stored on the heap.
The member variables of an object may be stored on the heap with the object itself. Whether this member variable is an original type or a reference type.
Static member variables follow the class definition and are also stored on the heap.
Objects stored on the heap can be accessed by all threads that hold references to the object. When a thread can access an object, it can also access the member variables of the object. If two threads invoke the same method on the same object at the same time, they will all access the object's member variables, but each thread has a private copy of the local variable.
Demonstrates the points mentioned above:
Two threads have local variables for some columns. One of the native variables (local Variable 2) executes a shared object on the heap (object 3). These two threads have different references to the same object, respectively. These references are local variables and are therefore stored on the thread stack of the respective thread. These two different references point to the same object on the heap.
Note that this shared object (object 3) holds Object2 and Object4 a reference as its member variable (the arrows in Object3 point to Object2 and Object4). By referencing these member variables in OBJECT3, these two threads can access both OBJECT2 and OBJECT4.
This diagram also shows a local variable pointing to two different objects on the heap. In this case, a reference to two different objects is not the same object. Theoretically, two threads can access both Object1 and OBJECT5, if two threads have references to two objects. But in, each thread has only one reference to one of the two objects.
So what type of Java code will cause the above memory graph? As shown below:
Public classMyrunnableImplementsRunnable () { Public voidrun () {methodone (); } Public voidMethodOne () {intLocalVariable1 = 45; Mysharedobject LocalVariable2=mysharedobject.sharedinstance; //.... Do more with local variables.Methodtwo (); } Public voidMethodtwo () {Integer localVariable1=NewInteger (99); //.... Do more with local variable. }} Public classMysharedobject {//static variable pointing to instance of Mysharedobject Public Static FinalMysharedobject sharedinstance =NewMysharedobject (); //member variables pointing to both objects on the heap PublicInteger Object2 =NewInteger (22); PublicInteger Object4 =NewInteger (44); Public LongMember1 = 12345; Public LongMember2 = 67890;}
If two threads execute the method at the same time run() , the scenario shown will appear. Method run() calls methodOne() the method, calling the method methodOne() methodTwo() .
methodOne()Declares a local variable of the original type and a local variable of a reference type.
Each thread executes a methodOne() localVariable1 localVariable2 private copy that will be created on the thread stack on which they correspond. localVariable1variables are completely independent of each other and only "live" on the thread stack of each thread. One thread cannot see the changes made by another thread to its localVariable1 private copy.
Each thread will methodOne() also create their own copies when executed localVariable2 . However, the localVariable2 different copies of two point to the same object on the heap. The code localVariable2 points to an object reference through a static variable setting. There is only one copy of the static variable, and this copy is stored on the heap. Therefore, localVariable2 the two copies point to the MySharedObject same instance of the static variable that is pointed to. MySharedObjectinstances are also stored on the heap. It corresponds to the Object3 in.
Note that MySharedObject A class also contains two member variables. These member variables are stored on the heap as this object. These two member variables point to a further two Integer objects. These Integer objects correspond to the Object2 and Object4 in.
Note that you methodTwo() create a local variable that is named localVariable . This member variable is an Integer object reference that points to an object. This method sets the localVariable1 reference point to a new Integer instance. When executing methodTwo a method, the localVariable1 reference will hold a copy of each thread. These two Integer object instantiations will be stored on the heap, but each time this method is executed, this method creates a new Integer object, and two threads executing this method will create two different Integer instances. The methodTwo Object1 method creates Integer objects that correspond to the in and Object5.
Also, a MySharedObject member variable of two types in a class is of the long original type. Because these variables are member variables, they are allowed to remain on the heap as the object is stored, and only local variables are stored on the thread stack.
Hardware Memory Architecture
The modern hardware memory model is somewhat different from the Java memory model. It is also important to understand how the memory model architecture and the Java memory model work together with it. This section describes the common hardware memory architecture, and the following sections describe how Java memory works with it.
Here is a simple illustration of the modern computer hardware architecture:
A modern computer usually consists of two or more CPUs. Some of these CPUs also have multicore. From this you can see that it is possible to run multiple threads concurrently on a modern computer with two or more CPUs. It is no problem for each CPU to run one thread at a time. This means that if your Java program is multithreaded, the previous thread in your Java program may execute concurrently (concurrently) on each CPU.
Each CPU contains a series of registers, which are the basis of memory within the CPU. The CPU performs the operation on the register much faster than the speed performed on main memory. This is because the CPU access register is much faster than main memory.
Each CPU may also have a CPU cache layer. In fact, the vast majority of modern CPUs have a certain size of cache layer. The CPU accesses the cache layer faster than the main memory, but it is usually slower than accessing the internal registers. Some CPUs also have multilayer caches, but these are not so important to understand how the Java memory model interacts with memory. Just know that you can have a cache layer on the CPU.
A computer also contains a main memory. All CPUs have access to main memory. Main memory is usually much larger than the cache in the CPU.
Typically, when a CPU needs to read main memory, it reads the portion of main memory into the CPU cache. It may even read some of the contents of the cache into its internal registers, and then perform operations in the register. When the CPU needs to write the result back into main memory, it flushes the value of the internal register to the cache, and then refreshes the value back to main memory at some point in time.
When the CPU needs to store something in the cache layer, the content that is stored in the cache is usually flushed back to main memory. The CPU cache can write the data locally to its memory at some point, and flush its memory locally at some point. It will no longer read/write the entire cache at some point. Typically, the cache is updated in a smaller block of memory called "cache lines". One or more cache rows may be read to the cache, and one or more cache rows may be flushed back to main memory.
Bridging between the Java memory model and the hardware memory schema
As mentioned above, there is a difference between the Java memory model and the hardware memory architecture. The hardware memory schema does not differentiate between line stacks and heap. For hardware, all line stacks and heaps are distributed in the main. Partial-line stacks and heaps may sometimes appear in the CPU cache and in registers inside the CPU. As shown in the following:
When objects and variables are stored in various memory areas of the computer, some specific problems may arise. The main two aspects are as follows:
- The visibility of threads to shared variable modifications
- Race conditions occurs when reading, writing, and checking shared variables
Let's explain the following two questions specifically.
Shared Object Visibility
If two or more threads share an object without proper use of the volatile declaration or synchronization, a thread updating the shared object may be invisible to other threads.
Imagine that the shared object is initialized in main memory. A thread running on the CPU reads the shared object into the CPU cache. The object is then modified. As long as the CPU cache is not flushed to main memory, the modified version of the object is not visible to threads running on other CPUs. This approach may cause each thread to have a private copy of the shared object, with each copy remaining in a different CPU cache.
Indicate the situation. The thread running on the left side of the CPU copies the shared object into its CPU cache, and then modifies the value of the count variable to 2. This modification is not visible to other threads running on the right side of the CPU, because the value of the modified count has not been flushed back into main memory.
To solve this problem you can use the keywords in Java volatile . volatilekeywords can be guaranteed to read a variable directly from main memory, if the variable is modified, will always be written back to main memory.
Race Conditions
Race conditions can occur if two or more threads share an object, and multiple threads update the variable on the shared object.
Imagine if thread a reads a variable count of a shared object into its CPU cache. Imagine that thread B did the same thing, but it went to a different CPU cache. Now thread A will count add 1, and thread B does the same thing. Now it count has been added to two, one per CPU cache.
If these additions are executed sequentially, the variables count should be incremented two times, then the original value +2 is written back to main memory.
However, two increases are performed concurrently without proper synchronization. Whether thread A or thread B count writes the modified version back to main memory, the modified value is only 1 larger than the original value, although it is incremented two times.
Illustrates the scenario described above:
The Java synchronization block can be used to solve this problem. A synchronization block guarantees that only one thread can enter the critical section of the code at the same time. The synchronization block also guarantees that all the variables accessed in the code block will be read from main memory, and when the thread exits the synchronization block, all the updated variables are flushed back into main memory, regardless of whether the variable is declared volatile.
Java multithreaded-java memory model