Detailed explanation of JMM memory model

1, Overview

The full name of JMM is Java Memory Model (Java Memory Model). What is JMM and why should JMM be set up? To figure this out, we must first start with the computer hardware storage system.

2. Computer hardware storage system

Windows task manager
Mac Explorer

 

Window and Mac are two different operating systems, but it can be seen from the above figure that both have memory and cache. We all know that the computing speed of the CPU is the fastest. In order to solve the problem of unequal computing speed between the CPU and the memory, a layer of cache is added between the CPU and the memory. Another example is the demand for real development, commodity spikes, and inventory is stored in the DB. It is very time-consuming to take the inventory from the DB and modify it each time. To solve this problem, we need to add a layer between the DB and the user or Multi-level caching. This is the memory model of the operating system.

3、JMM

Java applications need to run on different operating systems, so they must abide by the memory model rules of the operating system. Although Java can directly reuse the memory model at the operating system level, the memory models of different operating systems are different. If the memory model at the operating system level is directly reused, it may cause the same set of codes to be unable to execute in a different operating system. Because the Java language is cross-platform, the JVM specification attempts to define a Java Memory Model (JMM) to shield the memory access models of various hardware and operating systems. This is the original intention of JMM design.

JMM itself is an abstract concept and does not really exist. It only describes a set of conventions or specifications. Through this set of specifications, the read and write access methods of various variables in the program (especially multi-threaded) are defined, and a thread is determined. The key technical points of writing to shared variables and how to become visible to another thread revolve around the three characteristics of atomicity, visibility, and ordering of multithreading.

So what can JMM do?

1. Realize the abstraction between threads and main memory through JMM

②. Shield the memory access differences of various hardware platforms and operating systems to achieve consistent memory access effects for Java programs on various platforms

4. Three characteristics of JMM

4.1. Visibility

It refers to whether other threads can immediately know the change when a thread modifies a shared variable. JMM stipulates that all variables are stored in the main memory. Each thread has a local memory (copies of shared variables). The order in which threads obtain shared variables: first obtain from the judgment local memory, if not, obtain from the main memory and store in the local memory. Threads modify the order of shared variables: first modify the local memory, and then synchronize the local memory to the main memory (time unknown)

Since the time when the system main memory shared variable is modified and written is uncertain, "dirty read" (the read data is not the latest) is likely to occur under multi-threaded concurrency, so each thread has its own local memory, and the thread The main memory copy of the variables used by the thread is saved in its own working memory. All operations (reading, assignment, etc.) on variables by the thread must be performed in its own local memory, and cannot be directly read and written in the main memory. Variables. Different threads cannot directly access variables in each other's local memory, and the transfer of variable values ​​between threads needs to be completed through the main memory.

4.2. Atomicity

For multiple executions of the same operation, either all succeed or all fail.

In Java, atomicity can be achieved by means of synchronized, various Locks, and various atomic classes.

Synchronized and various Locks can ensure that only one thread accesses the code block at any one time, so atomicity can be guaranteed. Various atomic classes use CAS (compare and swap) operations (may also use volatile or final keywords) to ensure atomic operations.

4.3. Orderliness

For the execution code of a thread, we always habitually think that the execution of the code is always executed in order from top to bottom. But in order to improve performance, compilers and processors usually reorder the specified sequence. The Java specification stipulates that the JVM thread maintains sequential semantics, that is, as long as the final result of the program is equal to the result of its sequential execution, the execution order of the instructions can be inconsistent with the order of the code. This process is called instruction reordering.

What are the advantages and disadvantages of instruction reordering?

The JVM can properly reorder the machine instructions according to the characteristics of the processor, so that the machine instructions are more in line with the execution characteristics of the CPU, and the machine performance can be maximized.

Instruction reordering can ensure consistent semantics of serial (single-threaded), but there is no obligation to ensure consistent semantics of multi-threaded access (that is, "dirty reads" may occur). Simply put, it is possible to execute more than two lines of irrelevant code. The first line is not executed first, it is not necessarily executed sequentially from top to bottom, and the execution order will be optimized. For example, the following code, because there is no correlation between the two lines of code, it is possible to execute the second line instead of the first line during execution.


        //第一行
        int x = 0;
        //第二行
        int y = 1;
        //第三行
        int z = x + y;

In a multi-threaded environment, threads are executed alternately. Due to the existence of compiler optimization rearrangement, it is uncertain whether the variables used in the two threads can guarantee consistency, and the result is unpredictable.

package com.lc.test03;

/**
 * @author liuchao
 * @date 2023/4/10
 */
public class Test01 {

    int x = 0;
    int y = 0;

    public static void main(String[] args) {
        Test01 test01 = new Test01();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                test01.set();
                test01.call();
            }, String.format("t-%", i)).start();
        }
    }

    public void set() {
        x = 2;
        y = 3;
    }

    public void call() {
        if (y == 3) {
            System.out.println("----在多线程下由于重排序的存在,输出的X值可能为0:" + x);
        }
    }

}

5. The variable reading process under the JMM specification

①, define that all variables are stored in physical main memory

②. Each thread has its own independent local memory, which stores a copy of the variables used by the thread (copied from the main memory)

③ All operations of threads on shared variables must first be performed in their own local memory and then written back to the main memory, and the main memory cannot be directly operated

④. The local memory between different threads is isolated, and the variable transfer between threads needs to be carried out through the main memory.

 

 

Guess you like

Origin blog.csdn.net/u011837804/article/details/130067493