Java Memory Model-re-sorting, java model sorting
Data Dependency
If two operations access the same variable and one of the two operations is a write operation, there is data dependency between the two operations. There are three types of Data Dependencies:
| Name |
Sample Code |
Description |
| Post-read |
A = 1; B =; |
Write a variable and read it again. |
| Write after writing |
A = 1; a = 2; |
Write a variable before writing it. |
| Write after reading |
A = B; B = 1; |
Read a variable and write it again. |
In the above three cases, the execution result of the program will be changed as long as the execution sequence of the two operations is re-ordered.
As mentioned above, the compiler and processor may re-Sort operations. The compiler and the processor follow the data dependency when re-sorting. the compiler and the processor do not change the execution sequence of the two operations with data dependency.
Note that the data dependency mentioned here is only applicable to the command sequence executed in a single processor and the operations executed in a single thread, data dependencies between different processors and between different threads are not considered by compilers and processors.
As-if-serial Semantics
The as-if-serial semantics means that the execution results of (single thread) programs cannot be changed no matter how the compiler and the processor re-order (to improve the degree of parallelism. The compiler, runtime, and processor must comply with the as-if-serial syntax.
To comply with the as-if-serial semantics, the compiler and the processor do not re-Sort operations with Data Dependencies because such re-sorting changes the execution result. However, if there is no data dependency between operations, these operations may be reordered by the compiler and the processor. For details, see the following code example for calculating the circular area:
double pi = 3.14; //Adouble r = 1.0; //Bdouble area = pi * r * r; //C
The data dependency of the preceding three operations is shown in:
As shown in, data dependency exists between A and C, and between B and C. Therefore, in the final execution of the command sequence, C cannot be reordered to the front of A and B (C to the front of A and B, and the program results will be changed ). However, there is no data dependency between A and B, and the compiler and processor can re-sort the execution sequence between A and B. Is the two execution sequence of the program:
As-if-serial semantics protects a single-threaded program and complies with the as-if-serial syntax compiler. runtime and the processor jointly create an illusion for programmers who write a single-threaded program: A single-threaded program is executed in the program order. The as-if-serial semantics eliminates the need for single-thread programmers to worry about duplicate sorting interfering with them, or worry about memory visibility.
Program sequence rules
According to the program sequence rules of happens-before, the sample code for calculating the area of the circle above has three happens-before relationships:
The 3rd happens-before relationships are derived based on the transmission of happens-before.
Here A happens-before B, but in actual execution, B can be executed before A (see the execution order after the above sorting ). As mentioned in chapter 1, if A happens-before B, JMM does not require A To be executed before B. JMM only requires that the previous operation (execution result) be visible to the next operation, and the previous operation is placed before the second operation in order. Here, the execution result of Operation A does not need to be visible to Operation B, and the execution result after operation A and operation B is reordered, the result is consistent with that of operation A and operation B in the order of happens-before. In this case, JMM considers this type of re-sorting not illegal (not illegal), and JMM allows this type of re-sorting.
In computer systems, software and hardware technologies share a common goal: to develop concurrency as much as possible without changing the execution results of programs. The compiler and processor follow this goal. From the definition of happens-before, we can see that JMM also follows this goal.
Influence of reordering on Multithreading
Now let's take a look at whether the re-sorting will change the execution result of the multi-threaded program. See the following sample code:
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 …… }}}
A flag variable is a flag used to identify whether variable a has been written. Assume that there are two threads A and B. A first executes the writer () method, and then thread B executes the reader () method. When thread B executes operation 4, can we see that thread A is writing to shared variable a in operation 1?
The answer is: not necessarily visible.
Since there is no data dependency between operation 1 and operation 2, the compiler and the processor can reorder these two operations. Similarly, Operation 3 and operation 4 have no data dependency, the compiler and the processor can also reorder these two operations. Let's take a look at the effect that may occur when operations 1 and 2 are re-ordered? See the following program execution sequence diagram:
As shown in, operations 1 and 2 are reordered. During program execution, thread A first writes the flag variable, and then thread B reads the variable. Because the condition is true, thread B reads variable. At this point, variable a is not written by thread A at all. Here, the semantics of the multi-threaded program is damaged by the re-sorting!
※Note: This document uses the Red Virtual arrow to indicate incorrect read operations, and the green virtual arrow to indicate correct read operations.
Next let's take a look at what will happen when operations 3 and 4 are re-ordered (with this re-sorting, we can explain the control of Dependencies by the way ). The following figure shows the execution sequence of the program after operation 3 and operation 4 are re-ordered:
In a program, Operations 3 and 4 have control dependencies. When there is a control dependency in the Code, the concurrency of the command sequence execution is affected. To this end, the compiler and the processor will use the speculative execution to overcome the impact of controlling the degree of parallelism on the degree of parallelism. Taking the speculative execution of the processor as an example, the processor of the execution thread B can read and calculate a * a in advance, and then temporarily Save the computing result to a reorder buffer (reorder buffer ROB) hardware cache. When the condition in operation 3 is true, the calculation result is written to variable I.
We can see that the speculative execution actually sorts operations 3 and 4. Here, the meaning of the multi-threaded program is broken!
In a single-threaded program, reordering operations with control dependencies exists, the execution results will not be changed (this is also the reason why the as-if-serial semantics allows re-sorting of operations with control dependencies); but in multi-threaded programs, re-sorting operations with control dependencies may change the execution results of programs.