Content navigation:
- Java memory model
- Hardware Storage Architecture
- The bridge between the Java memory model and the hardware storage system:
- The visibility of shared objects
- Competitive conditions
The Java memory model specifies how the JVM works in coordination with the computer storage System (RAM). The JVM is a virtual machine model, so this model naturally contains a memory model
Understanding the Java memory model is important for designing the correct concurrency program. The JVM specifies when and how different threads can see the read and write of shared variables, and how to synchronize access control for shared variables.
The original Java memory model was not intact. So he was changed in the java1.5. The following memory models are still used in java1.8.
Java memory model
The memory model in the JVM is divided into stacks and heaps (heap). Is the logical diagram of the memory
Each thread running in the JVM has its own stack space.
Each thread stack includes a pointer to the current run location of the method where the threads are called. We call it the stack pointer. When the thread runs his code, the stack pointer changes. Share a heap of space.
The line stacks also includes local variables for all the methods that are being run. A thread can only access his own stack. Local variables created by each thread are not visible to other threads. Even though two threads are running the same piece of code, they will only create local variables that belong to their own stack space. That is, each thread has his own local variable, which does not affect each other.
All local base data types (Boolean, Byte, short, char, int, long, float, double) are completely stored in the online stacks space, and other threads are not visible. A thread can pass copies of its own local base data to a thread, but they do not share it with each other.
The heap space holds all the objects in the application, regardless of which thread was created.
and contains basic data objects (eg. Byte,integer,long, etc.).
Whether the object is created as a local variable or as a member variable that has an object. He's all in the heap. That is, all objects are in heap space.
See:
A local variable might be a basic data type. It may also be a reference to an object. They all create their own line stacks space, referencing the real object that is pointing to the heap space.
A member method of an object. Also, these methods may include local variables. These local variables belong to the line stacks
A member variable of an object, whether it is a base type or a reference to an object. The interior of the object that has this heap space
The same object can be interviewed by a different thread that has a reference to the object. And be able to access the member variables of the object.
Assuming that two threads invoke the same object's member methods at the same time, they can access the object's member variables, but each has its own copy of the local variable.
The following figure illustrates the above:
Hardware Storage Architecture:
The following is a simple modern computer's storage structure:
Modern computers typically have 2 or so many other CPUs, some of which may be multicore.
The key point is that a multi-CPU computer agrees that multiple threads execute at the same time (true parallelism rather than concurrency). Each CPU executes a thread at a given time slice. Assuming that the Java program is multithreaded, each thread can execute at the same time on each CPU.
Each CPU includes a series of registers.
The CPU runs directly from the register much faster than it does from memory.
Each CPU may also have a CPU cache (our well-known cache). In fact, most modern CPUs have caches of different capacities. CPUs are accessed from the cache more quickly than from memory, but slower than registers. Some CPUs also have multilevel caches (L1, L2, and so on). However, this does not affect the understanding of the Java memory model, and all we need to know is that the cache is cached in another layer between the memory and the CPU.
Typically, the CPU needs to read a portion of the cache when visiting main memory. It is also possible to read a portion of the cache to the register and then run the operation. When the CPU needs to write the result of the operation back to main memory, it will first update the value from register to Cache,cache and then write back to main memory.
The values stored in the cache are usually written back to memory when the CPU needs to store something else.
The cache writes the contents of the store back to main memory at once. This process does not necessarily read and write the entire cache. Typically, a smaller unit in the cache is updated, called the cache lines buffer line. One or more rows of cache line will be read from main memory to the cache. Or it is written back to main memory from the cache.
The bridge between Java memory model and hardware storage System
As already mentioned, the Java memory model and the hardware storage system are different.
The hardware storage architecture does not differentiate between heaps and stacks.
At the hardware level, both the heap and the stack are allocated in main memory. A portion of the heap and stack may also be in the cache and register. For example, with:
When objects and variables are stored in different storage areas of the computer (Rejister, Cache, main-memory), a failure occurs.
There are two problems such as the following:
- The visibility of threads to read and write shared variables
- Competition conditions for read-write check shared variables
The visibility of shared objects
Assuming that two or more threads share an object without the use of a volatile declaration or synchronize synchronization, an update to a shared object by one thread may not be visible to other threads.
Imagine a shared object that was started with the main memory. The thread executing on the CPU1 reads the shared object into the cache.
Then make changes to the shared object.
Only if the cache has not been flushed to main memory, the object's change version number is not visible to threads that are executing on other CPUs. As a result, each thread uses its own copy of the shared object, which is stored in the respective CPU cache.
The picture below illustrates this situation.
A thread that executes on the left side of the CPU copies the shared object to its own CPU cache, and changes the count value to 2. This change is not visible to the thread executing on the right side of the CPU.
Because the update to count is not flushed to main memory.
To resolve the issue. We can use Java's volatilekeyword. Volatilekeyword can ensure that the declared variables are read directly from main memory or are updated directly in main memory without passing through the cache middle layer.
Competitive conditions
Updates to shared variables by multiple threads can also cause competition.
Assume that thread a reads the Count field of the shared variable to his cache, and thread B is also. Now thread A pairs count plus 1, and thread B is.
Now Var1 has been added two times per CPU at a time.
Assuming that these additions are run sequentially (in order: A read increament writeback-B read increament writeback), then the variable count will be added sequentially 12 times. Finally the result is the original value plus 2 write back to main memory.
However, suppose that two additions were run concurrently (in the cross order) without proper synchronization. So whether a or B is written back to main memory, the result is the original value plus 1. Although it was done two times plus.
The following is a diagram of the above problem:
To solve the problem, introduce Java's synchronized block to make a series of operations atomic, that is, not to be interrupted (similar to transaction transaction in the database), thus enabling different threads to repel each other on a block of code.
Translated original address: http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
Java memory model