In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces "what is the Happens-before principle". In the daily operation, I believe that many people have doubts about what is the Happens-before principle. The editor consulted all kinds of materials and sorted out a simple and easy-to-use method of operation. I hope it will be helpful to answer the doubts about "what is the Happens-before principle"! Next, please follow the editor to study!
The problem and perfect solution of JMM designer
In the last article, "step thousands of miles", we explained in detail the Java memory model and atomicity, visibility, and order. we learned about JMM and its three properties. In fact, from the point of view of JMM designers, visibility and ordering actually contradict each other:
On the one hand, for programmers, we want the memory model to be easy to understand and program, so the designers of JMM should provide programmers with sufficient memory visibility, which is technically called "strong memory model".
Compilers and processors, on the other hand, want the memory model to bind them as little as possible so that they can do as many optimizations as possible (such as reordering) to improve performance, so JMM designers should relax their restrictions on compilers and processors as much as possible, which is technically called the "weak memory model".
Starting from JDK 5, that is, in the JSR-133 memory model, we finally come up with a perfect solution to this problem, that is, the Happens-before principle. Happens-before literally translates as "occurring first". The definition of Happens-before relationship by "JSR-133:Java Memory Model and Thread Specification" is as follows:
1) if one operation Happens-before another operation, the execution result of the first operation will be visible to the second operation, and the execution order of the first operation comes before the second operation.
2) the existence of a Happens-before relationship between the two operations does not mean that the specific implementation of the Java platform must be executed in the order specified by the Happens-before relationship. If the results of the reordered execution are consistent with those performed according to the Happens-before relationship, then this reordering is not illegal (that is, JMM allows such reordering)
It is not difficult to understand that the first definition is JMM's commitment to a programmer's strong memory model. From the programmer's point of view, the Happens-before relationship can be understood as follows: if A Happens-before B, then JMM will assure the programmer that the result of the-An operation will be visible to B and that A will be executed before B. Note that this is just the guarantee that the Java memory model gives to programmers!
It is important to note that unlike as-if-serial semantics, which only work on a single thread, the two operations An and B mentioned here can be either within one thread or between different threads. That is, Happens-before provides a guarantee of memory visibility across threads.
For this Article 1 definition, let me give you an example:
/ / the following operations are performed in thread An I = 1; / a / / the following operations are performed in thread B j = I; / b / / the following operations are performed in thread C I = 2; / / c
Assuming that operation an in thread A Happens-before operation b of thread B, then we can determine that after operation b is executed, the value of the variable j must be equal to 1.
There are two bases for this conclusion: one is that according to the Happens-before principle, the result of an operation is visible to b, that is, the result of "iThread 1" can be observed; second, thread C has not yet run, and no other thread will modify the value of variable I after thread An operation ends.
Now consider thread C, we still keep a Happens-before b, while c appears between the operations of an and b, but there is no Happens-before relationship between c and b, which means that b does not necessarily see the result of c's operation. Then the result of the b operation, that is, the value of j, is uncertain, either 1 or 2, so this code is not thread-safe.
Let's take a look at the second definition of Happens-before, which is JMM's guarantee for the weak memory model of compilers and processors, which restricts the reordering of compilers and processors with sufficient operating space. In other words, JMM is actually following a basic principle: as long as the execution result of the program is not changed (that is, single-threaded programs and correctly synchronized multithreaded programs), the compiler and processor can optimize whatever they want.
The reason JMM does this is that programmers don't care whether these two operations are really reordered, but that the execution results cannot be changed.
The text may not be easy to understand. Let's give an example to explain the following article 2 definition: although there is a Happens-before relationship between the two operations, it does not mean that the specific implementation of the Java platform must be executed in the order specified by the Happens-before relationship.
Int a = 1; / / An int b = 2; / / B int c = a + b; / / C
According to the Happens-before rules (described below), there are three Happens-before relationships in the above code:
1) A Happens-before B
2) B Happens-before C
3) A Happens-before C
As you can see, of the three Happens-before relationships, the second and third are required, but the first is unnecessary.
That is, although A Happens-before B, the reordering between An and B does not change the execution result of the program at all, so JMM allows compilers and processors to perform such reordering.
Take a look at the following design of JMM for more intuitive purposes:
Picture source "the Art of Java concurrent programming"
In fact, can be so simple to understand, in order to prevent Java programmers from learning complex reordering rules and the specific implementation of these rules in order to understand the memory visibility guarantee provided by JMM, JMM has come out with such an easy-to-understand Happens-before principle, a Happens-before rule corresponds to the reordering rules of one or more compilers and processors, so we only need to understand Happens-before.
Picture source "the Art of Java concurrent programming"
8 Happens-before rules
"JSR-133:Java Memory Model and Thread Specification" defines the following Happens-before rules, which are the "natural" Happens-before relationships in JMM, which already exist without any synchronizer assistance and can be used directly in coding. If the relationship between two operations is not in this list and cannot be derived from the following rules, they are not guaranteed to be sequenced, and JVM can reorder them at will:
1) Program order rules (Program Order Rule): within a thread, according to the order of control flow, the operation written in front occurs first (Happens-before) in the operation written later. Note that we are talking about the order of control flow rather than the order of program code, because branches, loops, and other structures are considered.
This is easy to understand and conforms to our logical thinking. For example, the example we gave above:
Synchronized (this) {/ / lock if (x) automatically here
< 1) { x = 1; } } // 此处自动解锁 根据程序次序规则,上述代码存在 3 个 Happens-before 关系: A Happens-before B B Happens-before C A Happens-before C 2)管程锁定规则(Monitor Lock Rule):一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。这里必须强调的是 "同一个锁",而 "后面" 是指时间上的先后。 这个规则其实就是针对 synchronized 的。JVM 并没有把 lock 和 unlock 操作直接开放给用户使用,但是却提供了更高层次的字节码指令 monitorenter 和 monitorexit 来隐式地使用这两个操作。这两个字节码指令反映到 Java 代码中就是同步块 — synchronized。 举个例子: synchronized (this) { // 此处自动加锁 if (x < 1) { x = 1; } } // 此处自动解锁 根据管程锁定规则,假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 1,执行完自动释放锁,线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x == 1。 3)volatile 变量规则(Volatile Variable Rule):对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作,这里的 "后面" 同样是指时间上的先后。 这个规则就是 JDK 1.5 版本对 volatile 语义的增强,其意义之重大,靠着这个规则搞定可见性易如反掌。 举个例子:Suppose that after thread An executes the writer () method, thread B executes the reader () method.
According to the rules of procedure order: 1 Happens-before 2 Happens-before 3 Happens-before 4.
According to the volatile variable rule: 2 Happens-before 3.
According to the transitivity rule: 1 Happens-before 3 1 Happens-before 4.
That is, if thread B reads "flag==true" or "int I = a", then thread A's setting of "await 42" is visible to thread B.
Look at the following picture:
4) Thread startup rule (Thread Start Rule): the start () method of the Thread object occurs first in every action of this thread.
For example, after the main thread A starts the child thread B, child thread B can see all the operations of the main thread in front of the promoter thread B.
5) Thread termination Rule (Thread Termination Rule): all operations in a thread first occur in the termination detection of this thread. We can detect whether the thread has terminated execution by means such as whether the join () method of the Thread object ends or not, and the return value of the isAlive () of the Thread object.
6) thread interrupt rule (Thread Interruption Rule): the call to the thread interrupt () method first occurs in the code of the interrupted thread to detect the occurrence of the interrupt event, and the interrupt event can be detected by the interrupted () method of the Thread object.
7) object termination rule (Finalizer Rule): the initialization of an object (the end of constructor execution) occurs first at the beginning of its finalize () method.
8) transitivity (Transitivity): if operation An occurs first in operation B and operation B occurs in operation C first, it can be concluded that operation An occurs first in operation C.
"first occurrence in time" and "first occurrence"
Among the above eight rules, the priority of time is also mentioned constantly, so what is the difference between "first occurrence in time" and "Happens-before"?
Does an operation "happen first in time" mean that the operation will "happen first"? Can it be inferred from the fact that an operation "occurs first" that the operation must occur first in time?
Unfortunately, neither of these inferences is valid.
Give two examples to demonstrate:
Private int value = 0; / Thread A calls pubilc void setValue (int value) {this.value = value;} / / Thread B calls public int getValue () {return value;}
Suppose there are threads An and B, thread A calls setValue (1) first (in chronological order), and then thread B calls getValue () of the same object, what is the return value that thread B receives?
According to the above eight rules of Happens-before, let's analyze them in turn:
Since the two methods are called by threads An and B respectively and are not in the same thread, the program order rules do not apply here.
Since there is no synchronized synchronization block, lock and unlock operations will not occur naturally, so the pipe locking rule does not apply here.
Similarly, volatile variable rules, thread startup, termination, interrupt rules, and object termination rules have nothing to do with this.
Since there is no applicable Happens-before rule, the transitivity of rule 8 is impossible.
Therefore, we can determine that although thread A precedes thread B in terms of operation time, it cannot be said that A Happens-before B, that is, the result of A thread operation B may not be seen. Therefore, this code is not thread-safe.
It's easy to fix this problem, too? Since it doesn't meet the Happens-before principle, I'll just modify it to make it satisfied. For example, Getter/Setter methods are modified with synchronized, so that you can apply pipe locking rules; for example, you can define value as a volatile variable, so that you can apply volatile variable rules.
This example demonstrates that an operation "occurring first in time" does not mean that the operation will occur first (Happens-before).
Let's look at another example:
/ / the following operations execute int I = 1; int j = 2 in the same thread
Assuming that the two assignment statements in this code are in the same thread, then according to the program order rule, the operation of "int I = 1" occurs first (Happens-before) to "int j = 2", but remember the definition of Happens-before in article 2? Remember that JMM actually adheres to the principle that as long as the execution result of the program is not changed (that is, single-threaded programs and correctly synchronized multithreaded programs), the compiler and processor can be optimized.
Therefore, it is entirely possible that the code "int jacks 2" will be executed by the processor first, because it does not affect the final running result of the program.
So, this example demonstrates that an operation "Happens-before" does not necessarily mean that the operation occurs first in time.
In this way, summing up two examples, we can draw such a conclusion: there is basically no causal relationship between the Happens-before principle and the time sequence, so when we measure the concurrency security problem, we should try not to be disturbed by the time sequence, and everything must be based on the Happens-before principle.
Happens-before and as-if-serial
To sum up, I think I can understand Happens-before by reading the following sentence, which has appeared several times above: JMM actually follows a basic principle, that is, as long as the execution result of the program is not changed (refers to single-threaded programs and correctly synchronized multithreaded programs), the compiler and processor can be optimized.
Review the as-if-serial semantics again: no matter how much you reorder, the execution result of a program in a single-threaded environment cannot be changed.
Have you noticed? In essence, Happens-before relation and as-if-serial semantics are the same thing, which is to improve the parallelism of program execution as much as possible without changing the result of program execution. It's just that the latter can only work in a single thread, while the former can work in a correctly synchronized multithreaded environment:
As-if-serial semantics ensures that the execution results of programs in a single thread will not be changed, and Happens-before relations ensure that the execution results of correctly synchronized multithreaded programs will not be changed.
As-if-serial semantics creates an illusion for programmers who write single-threaded programs: single-threaded programs are executed in the order of the program. The Happens-before relationship creates an illusion for programmers who write correctly synchronized multithreaded programs: correctly synchronized multithreaded programs are executed in the order specified by Happens-before.
At this point, the study of "what is the Happens-before principle" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.