The interviewer asked me what JMM

The interviewer asked me what JMM

mind Mapping

 

 

 

The article has been included in Github selection, welcome to Star : github.com/yehongzhi/l...

Interviewer: Tell me what JMM is

I won't be sleepy if you fix this.

 

 

 

JMM is the Java memory model (java memory model). Because different hardware manufacturers and different operating systems have certain differences in memory access, various problems will occur when the same code runs on different systems. Therefore, the Java Memory Model (JMM) shields the memory access differences of various hardware and operating systems to achieve consistent concurrency effects for java programs on various platforms.

The Java memory model stipulates that all variables are stored in the main memory , including instance variables and static variables, but not local variables and method parameters. Each thread has its own working memory. The working memory of the thread saves a copy of the variables used by the thread and the copy of the main memory. The operation of the thread on the variables is carried out in the working memory . Threads cannot directly read and write variables in main memory .

Different threads cannot access the variables in each other's working memory. The transfer of variable values ​​between threads needs to be completed through the main memory.

If it sounds abstract, I can draw a picture for you to see, it will be more intuitive:

 

 

 

The working memory of each thread is independent, and thread operation data can only be performed in the working memory, and then flushed back to the main memory. This is the basic working mode of threads defined by the Java memory model.

A warm reminder, some people here will misunderstand the Java memory model as Java memory structure , and then answer the heap, stack, and GC garbage collection. In the end, it is far from the question the interviewer wants to ask. In fact, when you ask about the Java memory model, you want to ask questions about multithreading and Java concurrency .

Interviewer: What does JMM define

This simple, the entire Java memory model is actually built around three characteristics. They are: atomicity, visibility, and order. These three characteristics can be described as the foundation of the entire Java concurrency.

Atomicity

Atomicity means that an operation is indivisible and uninterruptible, and a thread will not be interfered by other threads during execution.

The interviewer took a pen to write a piece of code, can the following code guarantee atomicity ?

int i = 2;
int j = i;
i++;
i = i + 1;
复制代码

The first sentence is the basic type assignment operation, which must be an atomic operation.

The second sentence reads the value of i first, and then assigns it to j. The two-step operation does not guarantee atomicity.

The third and fourth sentences are actually equivalent. First read the value of i, then +1, and finally assign the value to i. This is a three-step operation, and atomicity cannot be guaranteed.

JMM can only guarantee basic atomicity. To ensure the atomicity of a code block, two bytecode instructions, monitorenter and moniterexit, are provided, that is, the synchronized keyword. Therefore, operations between synchronized blocks are all atomic.

Visibility

Visibility means that when a thread modifies the value of a shared variable, other threads can immediately know that it has been modified. Java uses the volatile keyword to provide visibility. When a variable is modified by volatile, the variable will be refreshed to the main memory immediately after being modified. When other threads need to read the variable, they will go to the main memory to read the new value. And ordinary variables cannot guarantee this.

In addition to the volatile keyword, final and synchronized can also achieve visibility.

The principle of synchronized is that the shared variable must be synchronized to the main memory before entering the unlock after execution.

The final modified field, once the initialization is completed, if no object escapes (referring to the object can be used by other threads after the initialization is completed), then it is visible to other threads.

Orderliness

In Java, synchronized or volatile can be used to ensure the orderliness of operations between multiple threads. The realization principle is somewhat different:

The volatile keyword uses a memory barrier to prohibit instruction reordering to ensure order.

The principle of synchronized is that after a thread is locked, it must be unlocked before other threads can relock, so that the code block wrapped by synchronized is executed serially between multiple threads.

Interviewer: Tell me about the eight memory interactive operations

Okay, interviewer, there are 8 kinds of memory interaction operations. Let me show you a picture:

 

 

 

  • lock (lock), acting on variables in the main memory , marking the variable as a thread exclusive state.
  • read (read), acting on the main memory variables, the values of the variables transmitted from the main memory to the working memory of the thread, so that the next load operation uses.
  • load, which acts on the variables of the working memory , and puts the variables of the main memory of the read operation into the variable copy of the working memory.
  • use (use), acting on the working memory variable, the variable transmission to working memory execution engine, whenever the virtual encounters a bytecode instructions need to use the value of this variable will be performed during operation.
  • assign (assignment), which acts on the variable of the working memory , it assigns a value received from the execution engine to the variable copy of the working memory, and will execute it whenever the virtual machine encounters a bytecode instruction that assigns a value to the variable This operation.
  • Store (storage), a variable acting on the working memory , it transfers the value of a variable from the working memory to the main memory for subsequent write use.
  • write: Act on variables in the main memory . It puts the value of the variable obtained from the working memory in the store operation into the variable in the main memory.
  • Unlock (unlock): A variable that acts on the main memory . It releases a variable that is in a locked state, and the released variable can be locked by other threads.

Let me add the rules established by JMM for 8 kinds of memory interaction operations:

  • One of the read, load, store, and write operations is not allowed to appear alone, that is, load must be performed after the read operation, and write must be performed after the store operation.
  • The thread is not allowed to discard its latest assign operation, that is, after the variable data in the working memory has changed, it must inform the main memory.
  • Threads are not allowed to synchronize data without assign from working memory to main memory.
  • A new variable must be born in the main memory, and it is not allowed to directly use an uninitialized variable in the working memory. That is, load and assign operations must be performed before use and store operations are performed on variables.
  • A variable can only be locked by one thread at a time. After multiple locks, you must perform the same number of unlocks to unlock.
  • If a lock operation is performed on a variable, the value of this variable in all working memory will be cleared. Before the execution engine uses this variable, the value of the variable must be re-initialized by the load or assign operation.
  • If a variable is not locked, it cannot be unlocked. It is also not possible to unlock a variable locked by other threads.
  • Before a thread can unlock a variable, it must first synchronize the variable back to main memory.

Interviewer: Tell me about the volatile keyword

Inner heart: This can be the highlight, but it can't go wrong~

Many concurrent programming uses the volatile keyword, and its main functions include two points:

  1. Ensure the visibility of variables between threads.
  2. The CPU is prohibited from reordering instructions.

Visibility

Volatile modified variable, when a thread changes the value of the variable, other threads are immediately visible. Ordinary variables need to be re-read to get the latest value.

The process of volatile to ensure visibility is probably this process:

 

 

 

Does volatile guarantee thread safety?

Let me start with the conclusion. Volatile cannot guarantee thread safety.

How to prove it, we can see the results of the following piece of code:

/**
 * @author Ye Hongzhi 公众号:java技术爱好者
 **/
public class VolatileTest extends Thread {

    private static volatile int count = 0;

    public static void main(String[] args) throws Exception {
        Vector<Thread> threads = new Vector<>();
        for (int i = 0; i < 100; i++) {
            VolatileTest thread = new VolatileTest();
            threads.add(thread);
            thread.start();
        }
        //等待子线程全部完成
        for (Thread thread : threads) {
            thread.join();
        }
        //输出结果,正确结果应该是1000,实际却是984
        System.out.println(count);//984
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                //休眠500毫秒
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
            count++;
        }
    }
}
复制代码

Why does volatile not guarantee thread safety?

Very simple, visibility does not guarantee the atomicity of the operation. As mentioned earlier, count++ is not an atomic operation. It will be treated as three steps. First read the value of count, then +1, and finally assign the value back to the count variable. If you need to ensure thread safety, you need to use the synchronized keyword or lock to lock the count++ code:

private static synchronized void add() {
    count++;
}
复制代码

Disallow instruction reordering

First, let's talk about the as-if-serial semantics. No matter how the reordering is done, the execution result of the (single-threaded) program cannot be changed.

In order to make the instructions more in line with the execution characteristics of the CPU, maximize the performance of the machine, and improve the execution efficiency of the program, as long as the final result of the program is equal to the result of its sequentialization, the order of execution of the instructions can be inconsistent with the logical order of the code. This process is called instruction reordering .

There are three types of reordering, namely: compiler reordering, instruction-level parallel reordering, and memory system reordering. The whole process is as follows:

 

 

 

There is no problem with instruction reordering in a single thread, it will not affect the execution result, and it also improves performance. But in a multithreaded environment, there is no guarantee that it will not affect the execution result.

Therefore, in a multithreaded environment, instruction reordering needs to be prohibited .

The volatile keyword prohibits instruction reordering has two meanings:

  • When the program executes the read operation or write operation of the volatile variable, all the changes in the previous operation must have been carried out, and the result has been visible to the subsequent operation, and the subsequent operation must have not been carried out.

  • When optimizing instructions, you cannot place the statements that access volatile variables behind them for execution, and you cannot place the statements behind volatile variables before them for execution.

Here is an example:

private static int a;//非volatile修饰变量
private static int b;//非volatile修饰变量
private static volatile int k;//volatile修饰变量

private void hello() {
    a = 1;  //语句1
    b = 2;  //语句2
    k = 3;  //语句3
    a = 4;  //语句4
    b = 5;  //语句5
    //以下省略...
}
复制代码

Variables a and b are non-volatile modified variables, and k is modified with volatile. So statement 3 cannot be placed before statements 1 and 2, nor can it be placed after statements 4 and 5. But the order of sentences 1 and 2 cannot be guaranteed. Similarly, the order of sentences 4 and 5 cannot be guaranteed.

Also, when statement 3 is executed, statements 1, 2 are definitely executed, and the execution results of statements 1, 2 are visible to statements 3, 4, and 5.

What is the principle of volatile prohibiting instruction reordering

First, let me talk about memory barriers. Memory barriers can be divided into the following categories:

  • LoadLoad barrier: For such statements Load1, LoadLoad, Load2. Before the data to be read by Load2 and subsequent read operations are accessed, ensure that the data to be read by Load1 has been read.

  • StoreStore barrier: For such statements Store1, StoreStore, Store2, before Store2 and subsequent write operations are executed, the write operations of Store1 are guaranteed to be visible to other processors.

  • LoadStore barrier: For such statements Load1, LoadStore, Store2, before Store2 and subsequent write operations are flushed out, ensure that the data to be read by Load1 has been read.

  • StoreLoad barrier: For such statements Store1, StoreLoad, Load2, ensure that Store1 writes are visible to all processors before Load2 and all subsequent read operations are executed.

Insert the LoadLoad barrier after each volatile read operation, and insert the LoadStore barrier after the read operation.

 

 

 

Insert a StoreStore barrier in front of each volatile write operation and a SotreLoad barrier at the back.

 

 

 

The general principle is this.

Interviewer: It’s pretty good, basically it’s covered, and it’s not too early. Let’s end today’s interview. Go back and wait for the notice~

to sum up

To learn concurrent programming, the java memory model is the first stop. The three characteristics of atomicity, orderliness, and visibility almost run through concurrent programming, which can be described as basic knowledge. Play a pavement for further study.

In this article, if you are interviewing, the focus is on how the Java Memory Model (JMM) works, the three major features, and the volatile keyword. Why do you like to ask the volatile keyword, because the volatile keyword can pull out many things, such as visibility, order, and memory barriers . The technical level of the interviewer can be seen sharply. After all, interviewers also want to efficiently screen out qualified talents.

The code of all the above examples has been uploaded to Github:

https://github.com/yehongzhi/mall

Just like it if you find it useful, your like is the biggest motivation for my creation ~

Refusing to be a salted fish, I am a programmer who strives to be remembered by everyone. See you next time! ! !

Guess you like

Origin blog.csdn.net/m0_46995061/article/details/108731046