[Multithreading]-java memory model (JMM memory model)

Preface

The content of the first three chapters of "The Art of Concurrent Programming in Java" is a bit messy. I'm trying my best to share the core knowledge points of the first three chapters. Here is the same idea as before. If it is a novice who has no foundation, Don't buy this book. It is recommended to read "The Beauty of Java Concurrent Programming".

One, the basis of the Java memory model

I put forward a concept like this before to simplify everyone's understanding of threads:

No matter how complex business processing we do at the coding layer, there are only three tasks for the computer to do:

  1. Read data in memory
  2. CPU calculation data
  3. Write the operation result back to memory

A thread task actually completes the above three tasks in the bottom layer of the computer. We can describe the above relationship through a simple computer model:
Insert picture description here

Here our thread is responsible for getting data from the main memory and then handing it to the CPU for processing, and returning the data to the main memory after the CPU processing is over. So in this process, how does JMM (JAVA Memory Model translate: JAVA memory model) design thread memory model to realize the ability of data handling? Here we can look at the following JMM basic memory model:

Insert picture description here

Here, if thread A wants to complete the assignment operation of i=1, it needs to go through the following process:

  1. Thread A reads int i in the main memory, and saves a copy of int i to the private memory space of thread A
  2. Thread A assigns a new value of 0 to i , and then writes the updated i back to memory

Although the above process seems very simple, there will be problems if it is concurrent:

  1. Thread A reads int i in the main memory, and saves a copy of int i to the private memory space of thread A
  2. Thread B wants to execute the instruction i = i+1, but thread A has not yet written the updated value back to main memory
  3. Thread B reads int i = 0 and writes a copy of the read shared variable into its own private memory space
  4. Thread A completes the assignment of i=1 and writes the result back to main memory
  5. Thread B gets its own private memory variable i=0 at this time to calculate i=i+1 and get the result 1.
  6. Thread B writes the result back to main memory

The result of the operation we expect should be i=2, but because thread B was not able to read the updated variable value of thread A in time, there was a result that we read i=1. This is undoubtedly thread-unsafe, so Java's In order to solve this problem, the designer also proposed some solutions.

Second, the sequential consistency model

1. What is the sequential consistency model

When the synchronization relationship between threads is not handled well, there will be data competition. The Java memory model defines data competition as follows:

Write a variable
in thread A and read the same variable in thread B. There
is no synchronization between writing and reading.

And obviously after data competition occurs, the code does not get the results we expected very well, so we need a means to ensure that data competition does not occur. JMM proposes at this time that if there is a correct synchronization relationship between threads, then the execution of the program has sequential consistency (Sequentially Consistent) , that is, the expected running result of the program should be the same as the running result of the program under the sequential consistency memory model. the same. But it should be noted here that the sequential consistency model is only a reference model put forward by computer scientists too idealistically. Although it provides programmers with a strong memory visibility guarantee, it is still not fully implemented by Java due to the following characteristics achieve:

  1. All operations in a thread must be executed in program order
  2. All threads can only see a single operation execution order. Under the sequential consistency model, all operations must be atomic and visible to all threads immediately after execution.

The above two features are undoubtedly in conflict with the design features of other parts of Java. For example, Java allows the CPU to reorder instructions. Although this improves the efficiency of the program, it obviously violates the requirements of the sequential consistency model. Therefore, the designer proposed a new specification to solve the place where the sequential consistency model cannot be satisfied in the Java design

2.as-if-serial

We can look directly at as-if-serial: just like serial . And obviously, as-if-serial requires that no matter how the JVM reorders the code, the result of its execution should be the same as the result of the thread serialization. The simple understanding is that no matter how the reordering is done, the result of a single-threaded operation is not allowed to change. This is a great proposal. Java also strictly abides by the rules it has set for itself. In a single thread, no matter what reordering of the code occurs, we can always maintain the visibility of the data, which is why We seem to have some hallucinations, do we not need to care about the interference of reordering? Then let's take a look at a Demo that was used before volatile:

public class VolatileOrderliness {
    
    

	private static long a = 0;
	private static long b = 0;
	private static long c = 0;
	private static long d = 0;
	private static long e = 0;
	private static long f = 0;
	private static long g = 0;
	private static long h = 0;

	public static long count = 0;

	public static void main(String[] args) {
    
    
		// 由于cpu指令重排发生存在概率 所以使用死循环调用 然后再出现的时候通过break跳出循环
		for (;;) {
    
    
			a = 0;
			b = 0;
			c = 0;
			d = 0;
			e = 0;
			f = 0;
			g = 0;
			h = 0;
			count++;
			Thread t1 = new Thread(() -> {
    
    
				a = 1;
				c = 101;
				d = 102;
				g = b;
			});
			Thread t2 = new Thread(() -> {
    
    
				b = 1;
				e = 201;
				f = 202;
				h = a;
			});
			t1.start();
			t2.start();
			try {
    
    
				t1.join();
				t2.join();
				String result = "count = " + count + " g = " + g + ", h=" + h;
				if (g == 0 && h == 0) {
    
    
					// 当g 和h 都出现0的时候 一定是发生了指令重排序
					System.err.println(result);
					break;
				} else {
    
    
					System.out.println(result);
				}
			} catch (InterruptedException e1) {
    
    
				e1.printStackTrace();
			}

		}

	}

}

Operation result:
Insert picture description here
It is obvious here that after we operate on shared variables through two threads, JVM does not guarantee the visibility of data for us in a parallel state. For this point, java adopts a new concept after JDK1.5: happens -before

3.happens-before

Let's understand through the examples in Chapter 3.7.1 of "The Art of Java Concurrent Programming":

double pi = 3.14 ;  // 代码A 定义pi的值为3.14
double r = 1.0 ;    //代码B定义圆的半径为1.0
double area = pi*r * r ;//代码C 计算圆的面积

If we follow the happens-before principle, we can understand it this way:

  • A happens-before B
  • B happens-before C

In order to achieve the above effects, JMM designed the following operating model:
Insert picture description here
Here we see that the memory model satisfies the programmer's strong visibility of memory under the condition that the processor is very small. In essence, happens-before is right as -If-serial semantic supplement.
According to "JSR-133: Java Memory Model and Thread Specification", the happens-before rules are defined in detail:

  1. Program sequence rules: each operation happens-before in a thread and any subsequent operations in the thread
  2. Monitor lock rule: unlocking a lock happens-before and then locking the lock
  3. Volatile variable rules: write operations to a volatile domain happens-before and any subsequent read operations to this volatile domain
  4. Transitivity: A happens-before B, B happens-before C, then A happens-before C

It should be noted here that you must not think that happens-before refers to A happens-before B means that A must run before B, but that the results of program A are visible to program B.

Three, postscript

At this point, the knowledge points related to the Java memory model have basically been summarized. The content of this chapter is theoretical and difficult to understand. If you feel unclear or feel that there are problems with what I said, please leave a message in the comment area, I will Reply in the first time.
good luck!

Guess you like

Origin blog.csdn.net/xiaoai1994/article/details/111310870