Java concurrent reading notes: JMM and reordering

Java Memory Model (JMM)

The Java Memory Model (JMM) defines the access rules for each variable in the program, that is, the underlying details of storing variables into memory and fetching variables from memory in the virtual machine.

In Java, all instance fields, static fields and array elements are stored in heap memory. There are shared variables between threads in the heap. These variables are shared variables.

Local variables, method definition parameters and exception handler parameters are not shared among threads, and they do not have memory visibility problems.

JMM abstract structure

Figure 3-1 from the art of Java Concurrent Programming

The above figure is an abstract structure. A main memory containing shared variables has a copy of shared variables in the local memory of each thread to improve efficiency. The Java Memory Model (JMM) defines the abstract relationship between threads and main memory. The abstract meaning does not exist, but also covers other specific parts, such as cache, write cache area Registers, etc.

How do threads a and B communicate at this time?

To be clear, JMM ensures memory visibility by controlling the interaction between main memory and local memory of each thread.

Reorder

In order to optimize program performance, the compiler and processor will reorder the instruction sequence, which may lead to memory visibility problems in multithreading.

Source code - > final instruction sequence

The following figure shows the art of Java Concurrent Programming 3-3

Compiler reordering

JMM prohibits certain types of compiler reordering for compiler reordering rules.

Processor reordering

For processor reordering, JMM's processor reordering requires the java compiler to insert specific types of memory barrier instructions when generating instruction sequences to prohibit specific types of processor reordering.

Data dependency

If two operations access the same variable and one of the two operations is a write operation, there is a data dependency between the two operations.

The compiler and processor will abide by the data dependency and will not change the execution order of the two operations with data dependency. (for instruction sequences executed in a single processor and operations executed in a single thread)

Considering the abstract memory model, modern processors deal with the process of data transfer between threads: write the data to the write buffer, refresh the write buffer in a batch manner, merge the multiple writes of the write buffer to the same memory address, and reduce the occupation of the memory bus. However, each write buffer is only visible to its processor, and the processor's read / write operations to memory may change.

as-if-serial

No matter how you reorder, the execution results of (single thread) programs cannot be changed. Similarly, operations with data dependency will not be reordered. Accordingly, if there is no data dependency, they will be reordered.

double pi = 3.14; // A 
double r = 1.0; // B 
double area = pi * r * r; // C

Obviously, the as if serial semantics well protects the above single thread, making us think that the program is executed in the order of a - > b - > C.

happens-before

Starting from jdk5, Java uses the new jsr-133 memory model and uses the concept of happens before to explain the memory visibility between operations.

There is a simple example to understand the so-called visibility and happens before "happen first" rules.

i = 1;  //在线程A中执行
j = i;   //在线程B中执行

We analyze the value of J in thread B: if a happens before B, the result of I = 1 in operation a is visible to B. at this time, j = 1 is accurate. However, if there is no happens before relationship between them, the value of J is not necessarily 1.

Rules of happens before

The following is derived from understanding the Java virtual machine, which means that if the following rules are not followed, the compiler and processor will reorder at will.

Definition of happens before relationship

for instance:

private int value = 0;

public void setValue(int value){
    this.value = value;
}
public int getValue(){
    return value;
}

Suppose there are two threads at this time. Thread a first calls setValue (5), and then thread B calls getValue of the same object. Consider the value returned by B:

Check one by one according to multiple rules of happens before:

To sum up, on the timeline, operation a occurs before operation B, but they do not meet the happens before rule, so it is impossible to determine the result obtained by thread B. therefore, the above operations are not thread safe.

How to modify it? We need to find a way to make the two operations meet the happens before rule. For example:

Effect of reordering on Multithreading

Consider the impact of reordering on Multithreading: if there are two threads, a executes the writer () method first, and B executes the reader () method again.

class ReorderExample { 
    int a = 0; 
    boolean flag = false; 
    public void writer() { 
        a = 1;              // 1
        flag = true;        // 2 
    }
    Public void reader() { 
        if (flag) {         // 3 
            int i = a * a;  // 4
            …… 
        } 
    } 
}

Before learning about reordering, I will not hesitate to think that when operation 4 is run, the modified a = 1 has been read, and I is 1 accordingly. However, due to the existence of reordering, the results may be unexpected.

Operations 1 and 2, 3 and 4 have no data dependency. The compiler and processor can reorder them, which will lead to deviation from the original semantics of multithreading.

Sequential consistency

Data competition and order consistency

The above example has typical data competition:

We should ensure the correct synchronization of multithreaded programs and ensure that there is no data competition.

Sequential consistent memory model

These mechanisms can actually serialize all memory read and write operations of all threads.

The sequential consistency memory model and JMM have the same results for correctly synchronized programs. However, for unsynchronized programs, the program execution order will be different.

JMM process synchronizer

For correctly synchronized programs (such as adding synchronized keyword modification to methods), JMM will reorder the code within the critical area without changing the program execution results, which does not facilitate the optimization of compiler and processor.

JMM handling asynchronous programs

JMM provides minimal security for multithreaded programs that are not synchronized or are not synchronized correctly.

1、 What is minimum security? JMM ensures that the value read by the thread is either the value written by a previous thread, Or the default value (0, false, null). Second, how to achieve minimum security? When JMM allocates objects on the heap, it will first clear the memory space, and then allocate objects on it. Therefore, when allocating objects with cleared memory space, the default initialization of the domain has been completed (0, false, null). Third, JMM handles the characteristics of asynchronous programs?

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>