Happen-Before is a core concept of the JMM, so you need to understand the Java memory model before you can understand the Happen-Before principle.

JMM memory model

The Java memory model is a concurrency model of shared memory, and the implicit communication between threads is mainly accomplished through read-write shared variables. Shared variables in Java are stored in memory, and multiple threads have their working memory. The way it works is to take the variables out of the shared memory and put them into the working memory. After the operation is completed, the latest variable is put back into the shared variable, so that other threads can get the latest shared variable.

Viewed horizontally, it looks as if threads A and B are implicitly communicating via shared variables. An interesting problem with this is that if thread A updates the data and does not write it back to main memory in time, then thread B reads stale data, which is A dirty read. To avoid dirty reads, either the synchronization mechanism (which controls the relative order in which operations occur between different threads) or the volatile keyword enables each volatile variable to force a flush to main memory, making it visible to each thread.

reorder

Compilers and processors often reorder instructions when executing programs to improve performance. Generally, resort can be divided into the following three types: as shown in the figure, 1 belongs to compiler resort, while 2 and 3 are collectively referred to as processor resort.

These reorders can lead to thread-safety problems, a classic example of which is the DCL problem. The JMM compiler reorder rules disallow certain types of compiler reorders; For handler reordering, the compiler inserts memory barrier instructions to disable certain handler reorders when generating instruction sequences.

(1) Compiler optimized reorder. The compiler can rearrange the execution order without changing the semantics of the single-threaded program. (2) Instruction level parallel reordering. Modern processors use instruction-level parallelism to overlap multiple instructions. If there is no data dependency, the processor can change the execution order of the corresponding machine instructions. (3) Memory system reordering. Because the processor uses caching and read/write buffers, the load and store operations can appear to be performed out of order.

Here’s an example:

Since A and B have no relationship to each other and no relationship to the final result, the execution order between them can be reordered. So the execution order could be A->, B->, C or B->, A->, C and the final result would be 3.14, that is, there would be no data dependencies between A and B.

What is to happen – before

JMM can provide A guarantee of cross-thread memory visibility to programmers through A happens-before relationship (if there is A happens-before relationship between A's write and B's read, even though A and B are executed in different threads, But the JMM assures the programmer that the A operation will be visible to the B operation.

1) If an operation happens-before another operation, the result of the execution of the first operation will be visible to the second operation, and the execution order of the first operation will precede that of the second operation.

2) The existence of a happens-before relationship between two operations does not mean that the concrete implementation of the Java platform must be executed in the order specified by the happens-before relationship. If the result of the reorder is the same as the result of the happens-before relationship, the reorder is not illegal (that is, the JMM allows the reorder).

Specific rules:

(1) Rules of program sequence: every operation in a thread happens-before any subsequent operation in the thread.

(2) Monitor lock rule: the unlocking of a lock happens before the subsequent locking of the lock.

The rule for volatile variables: A write to a volatile field happens-before any subsequent read to that field.

(4) Transitivity: If A happens-before B and B happens-before C, then A happens-before C.

(5) Start () rule: If thread A executes the operation threadb.start () (which starts thread B), then the operation of thread A happens-before any operation in thread B.

If thread A executes the operation threadb. Join() and returns successfully, then any operation in thread B happens to return successfully before thread A returns from the operation threadb. Join().

If the thread is interrupted(), the call to the interrupted() method will occur before the interrupted thread’s code detects the interruption.

(8) Object Finalize rule: The completion of initialization of an object (the end of constructor execution) precedes the beginning of the Finalize () method in which it occurs.

Using the program order rule (rule 1), there are three happens-before relationships:

A happens-before B; B happens-before C; A happens-before C. The third relation here is inferred using transitivity. The third relation here is inferred using transitivity.

A happens-before B. Definition 1 requires that the execution result of A is visible to B, and the execution order of A operation precedes B operation. However, according to the second clause in the definition, A and B operations do not have data dependence on each other, and the execution order of the two operations will not have an impact on the final result. Allows operations A and B to reorder, A happens-before relationship that does not represent the final order of execution.