Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the function of Happens-before?

2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "what is the function of Happens-before". In daily operation, I believe that many people have doubts about the role of Happens-before. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful for you to answer the doubts about "what is the role of Happens-before?" Next, please follow the editor to study!

Write at the front

In the previous article, there are three sources of concurrent Bug. Please open your eyes and see clearly that they talk about three issues of visibility / atomicity / order, which usually go against our intuition and mode of thinking, which leads to a lot of concurrent Bug.

To solve the shortcomings of CPU, memory and IO, cache is added, but this leads to visibility problems.

Compiler / processor unauthorized optimization (Java code becomes Java bytecode after compilation, bytecode is loaded into JVM by class loader, JVM executes bytecode, and eventually needs to be converted into assembly instructions to be executed on CPU), resulting in ordering problems

The original intention is good, but give rise to new problems, the most effective way is to prohibit caching and compilation optimization, although the problem can be solved, but "back to the original starting point, standing blankly in front of the mirror" is very embarrassing, the performance of our program is worrying.

Solution

As programmers, we don't want to write bug that affects KPI, so we want the memory model to be easy to understand and easy to program. This requires writing code based on a strong memory model.

As compilers and processors do not want outsiders to say that its processing speed is slow, so they want the memory model to bind them as little as possible, and it can be optimized by them without authorization, which needs to be based on a weak memory model.

As the saying goes, "there is nothing that cannot be solved by a meeting. If there is, then open it again"?

JSR-133 experts have a new idea: since caching and compilation optimization cannot be completely disabled, then disable caching and compilation optimization as needed, and add some constraints as needed, including the three keywords volatile,synchronized,final briefly mentioned in the previous article, as well as Happens-Before principles (including visibility and ordering constraints) that you may have heard of. Happens-before rules are also the main content of this chapter.

In order to meet the strong needs of both and take into account the emotions of both sides, so: JMM told the programmer a white lie: "will strictly abide by the Happpen-Befores rules, will not reorder" to reassure the programmer, but privately have their own strategy:

For reordering that changes the result of program execution, JMM requires that compilers and processors must prohibit such reordering.

JMM does not require compilers and processors for reordering that does not change the result of program execution (JMM allows such reordering).

Let's use a picture to illustrate:

This is the white lie, although it is a lie, but it still takes care of the interests of the programmer, so we only need to understand the happens-before rules to be guaranteed (we have been pictured for a long time, do not know if the lie is located, welcome to leave a message)

Happens-before

Happens-before rules are mainly used to constrain two operations. There is a happens-before relationship between the two operations, which does not mean that the former operation must be performed before the latter operation. Happens-before only requires the previous operation (the result of execution) to be visible to the latter operation, (the first is visible to and ordered before the second).

Having said that, let's take a look at a small piece of code that takes you step by step into the Happen-Befores principle and see how it can be used to solve the problems of visibility and order:

Class ReorderExample {int x = 0; boolean flag = false; public void writer () {x = 42; / / 1 flag = true; / / 2} public void reader () {if (flag) {/ / 3 System.out.println (x); / / 4}

Suppose thread An executes the writer method, thread B executes the reader method, and the x printed out may be 0. as explained in the previous article: because codes 1 and 2 have no data dependencies, they may be reordered

Flag = true; / / 2x = 42; / / 1

So, when thread A writes flag = true but does not reassign x, thread B may have printed that x is 0

So try adding the volatile keyword to flag:

Volatile boolean flag = false

Even with the addition of the volatile keyword, this problem has not been solved before java1.5, but java1.5 and later versions have enhanced the semantics of volatile and solved the problem, which is inseparable from the constraints of Happens-before rules. There are a total of six rules, let's see.

Rules of procedure order

For each operation in a thread, happens-before first feels that this principle is "nonsense" in an ideal state, and contradicts the above-mentioned situation of reordering. Note that this is an operation in a thread, which actually implies the meaning of "as-if-serial": to put it bluntly, as long as the execution result is not changed, no matter how "sorting" it is, it is correct.

This rule is a basic rule, and happens-before is a multithreaded rule, so it has to be constrained with other rules to show its sequence. Don't worry, keep looking down.

Volatile variable rule

Writing to a volatile domain, happens-before to any subsequent reading of the volatile domain

I'll add two lines of code to the above program as an explanation:

Public class ReorderExample {private int x = 0; private int y = 1; private volatile boolean flag = false; public void writer () {x = 42; / / 1 y = 50; / / 2 flag = true / / 3} public void reader () {if (flag) {/ / 4 System.out.println ("x:" + x); / / 5 System.out.println ("y:" + y); / / 6}

This involves the memory-enhanced semantics of volatile. Let's take a look at a table:

Can you reorder the second operation? the first operation is ordinary read / write volatile read volatile write ordinary read / write-NOvolatile read NONONOvolatile write-NONO

As you can see from the last column of this table:

If the second operation is volatile, no matter what the first operation is, it cannot be reordered, which ensures that the operations before volatile writing will not be reordered to the code above after volatile writing. For example, codes 1 and 2 will not be reordered after code 3, but codes 1 and 2 may be reordered (no dependencies and will not affect the execution result). When it comes to this, is it already related to the rules of procedural order?

You can see from the penultimate row of this table:

If the first operation is volatile read, no matter what the second operation is, it cannot be reordered, which ensures that the operations after volatile reading will not be reordered before volatile reading. Take the above code, for example, code 4 reads volatile variables, and codes 5 and 6 are not reordered before code 4.

The implementation of volatile memory semantics is applied to the "memory barrier", because it is enough to write a separate chapter. Here, in order not to hide the halo of the protagonist Happens-before and maintain the continuity of understanding Happens-before, we do not explain too much.

At this point, if you look at this rule, it doesn't seem to solve any problem, because it has to be combined with the third rule to work.

Transitivity rule

If A happens-before B, and B happens-before C, then A happens-before C illustrates the above example directly above.

As can be seen from the above picture

X = 42 and y = 50 Happens-before flag = true, which is rule 1

Write variable (code 3) flag=true Happens-before read variable (code 4) if (flag), which is rule 2

According to rule 3 transitivity rule, x = 42 Happens-before read variable if (flag)

The mystery is about to be solved: if thread B reads that flag is true, then x = 42 and y = 50 must be visible for thread B, which is an enhancement of Java1.5 (the previous version can be reordered by ordinary variables and volatile variables)

Usually the above three rules are a kind of joint constraint, do you understand here? The rules are not over. Keep looking.

Monitor lock rule

Unlock a lock happens-before then add a lock to the lock

I think you should be most familiar with this rule, that is, to explain the keyword synchronized.

Public class SynchronizedExample {private int x = 0; public void synBlock () {/ / 1. Lock synchronized (SynchronizedExample.class) {x = 1; / / a pair of x assignments} / / 3. Unlock} / 1. Lock public synchronized void synMethod () {x = 2; / / a pair of x assignments} / / 3. Unlock}

The thread that acquires the lock first releases the lock after assigning the value to x, and then the other one acquires the lock. It must be as simple as that. Please check the above program with the following command to see the difference between synchronous blocks and synchronous methods converted into assembly instructions.

Javap-c-v SynchronizedExample

This is related to the semantics of synchronized. Partners can learn about it by themselves, and the contents of the lock will be explained in detail.

Start () rule

If thread An executes the operation ThreadB.start () (start thread B), then thread A's ThreadB.start () operates any operation in thread B, that is, after the main thread A promoter thread B, child thread B can see the operation of the main thread in front of the promoter thread B.

Public class StartExample {private int x = 0; private int y = 1; private boolean flag = false; public static void main (String [] args) throws InterruptedException {StartExample startExample = new StartExample (); Thread thread1 = new Thread (startExample::writer, "Thread 1"); startExample.x = 10; startExample.y = 20 StartExample.flag = true; thread1.start (); System.out.println ("main thread ends");} public void writer () {System.out.println ("x:" + x); System.out.println ("y:" + y); System.out.println ("flag:" + flag) }}

Running result:

Main thread ends x:10y:20flag:trueProcess finished with exit code 0

Thread 1 sees all the assignments before the main thread calls thread1.start (). There is no print of "main thread end". Do you know why? This daemon thread knowledge is relevant.

Join () rule

If thread An executes the operation ThreadB.join () and returns successfully, then any operation happens-before in thread B returns successfully from thread A from the ThreadB.join () operation, contrary to the start rule. The main thread A waits for child thread B to complete. When child thread B completes, the main thread can see the assignment operation of child thread B. Make a small change to the program, and you will understand it in seconds.

Public class JoinExample {private int x = 0; private int y = 1; private boolean flag = false; public static void main (String [] args) throws InterruptedException {JoinExample joinExample = new JoinExample (); Thread thread1 = new Thread (joinExample::writer, "Thread 1"); thread1.start (); thread1.join () System.out.println ("x:" + joinExample.x); System.out.println ("y:" + joinExample.y); System.out.println ("flag:" + joinExample.flag); System.out.println ("main thread ends");} public void writer () {this.x = 100 This.y = 200; this.flag = true;}}

Running result:

X:100y:200flag:true main thread ends Process finished with exit code 0

The words "main thread ends" are printed out, and it still has something to do with when the thread exits.

Summary

Happens-before focuses on solving the previous operation and the result is visible to the latter operation. I believe that you have learned something about Happens-before rules, which solve the visibility and ordering problems of multithreaded programming, but have not completely solved the atomicity problem (except synchronized).

Start and join rules are also one of the ways to solve the communication between main thread and child thread.

From the perspective of memory semantics, volatile write-read and lock release-acquisition have the same memory effect; volatile write and lock release have the same memory semantics; volatile read and lock acquisition have the same memory semantics. ⚠️ (hit the blackboard) volatile solves the visibility problem, synchronized solves the atomicity problem, this is definitely not the same thing, as will be explained in the following articles.

Soul questioning

What is the difference between synchronous blocks and synchronous methods after being compiled into CPU instructions?

Threads have Daemon (daemon thread) and non-Daemon threads. Do you know the exit strategy of threads?

Do you have any questions about Happens-before?

At this point, the study of "what is the role of Happens-before" 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.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report