Speaking of happen before, a lot of people know. However, because of the abstraction of the theory and the subtlety of semantics, the understanding of happen before often falls into the position of "nowhere near". This paper attempts to macro and multi-angle to analyze the many problems around happen before, so as to find out why we need happen before?
- Visibility
- Reorder
-happen before
- shared storage model vs. message model visibility
When it comes to visibility, many people immediately think of volatile, knowing that it is a way to solve the visibility between single variables and multithreading, but this is only 1 aspects of visibility.
In this paper, we try to discuss this problem from a more macroscopic point of view. As we all know, Java's multithreaded communication model is "shared storage Model", which corresponds to the "message model".
In my view, all of the "shared storage model", whether memory storage, or DB storage, from an abstract point of view, to solve 2 problems:
Problem 1: Mask Visibility--A (thread/client) writes data to a shared store and does not want B to see it, but B sees it. Why don't you let B see it.
Because at this time a data is written 1 and a half, b to read, will read dirty data. For example, the previous 64-bit long/double atomic problem, such as MySQL, one of the transactions written half, another transaction to read.
Our usual lock, whether it is DB lock or thread lock, is to solve this problem. Of course, it also solves the problem of 2.
Question 2: Open visibility--a writes the data to a shared store, which is visible when the subsequent B is read, but B is invisible. Why can't b see it.
Because A/b has its own cache. From the physical implementation point of view, the modern CPU has its own cache, from the abstract point of view, JMM, each thread has its own working memory.
The volatile mentioned earlier is to solve this problem. reordering
We all know that in order to improve the concurrency and efficiency of instruction execution as much as possible, both compilers and CPUs do reordering. In terms of the underlying implementation principle, there are a lot of reordering rules. But for programmers, when is the command to be queued and when not?
Here, I think we can use the "negative" approach to the problem: In addition to those who will not be rearrangement, the rest, may be rearrangement.
And which are not to be rearrangement.
(1) as-if-serial semantics-from the programmer's point of view, single-threaded programs are not to be rearrange. That is, on the underlying implementation, no matter how the rearrangement is done, the upper layer is guaranteed to appear in code order.
(2) with happen before semantics, that is, the synchronization mechanism: volatile, final, synchronized, lock, etc., will not be rearrangement.
In addition, it can be rearrangement.
In other words: compilers and CPUs only guarantee the reordering within a single thread, conforming to as-if-serial semantics. As for this reordering, the compiler and CPU are regardless of whether the other threads have an effect.
Because of the problem of synchronization between threads caused by reordering, programmers are required to solve them with various thread synchronization mechanisms themselves.
There are many previous examples of the problem of unsynchronized threads caused by reordering:
For example, thread-safe single case mode DCL problem,
For example Concurrenthashmap inside, get when, value = NULL, lock re-read the problem. happen before
What exactly is happen before? The happen before is a 1 set of rules defined by the JVM to address the above visibility and reordering issues.
The definition of the rule itself is simple: a happen before B, then A's action result is visible to B.
A happen before B is not to say that a must be performed before B. Instead, if a is executed before B, the result of A's operation must be visible to B.
The following are some of the commonly used happen before rules:
(1) single-threaded, the preceding instruction happen before subsequent instruction (which implies that, even if the preceding instruction may be executed in a single thread, the following instruction may be executed first.) Because of the reordering)
(2) writes to volatile, happen before all subsequent reads to volatile (meaning: corresponding to ordinary variables, no happen before relationship, 1 lines enters upgradeable written, 1 other threads to read, may not read)
(3) Initialization of the final domain, happen before all subsequent reads to the final domain (this sentence implies that for ordinary variables, the initialization process may be reordered outside the constructor.) That means that another thread gets the object, but the code inside the constructor may not have finished executing.
(4) Unlocks the lock, happen before subsequent lock to the lock. (The implication of this sentence is: because happen before of the partial order relationship, you can deduce, the 1th time lock, the result of the lock inside, for the 2nd lock inside the reading, must be visible)