Thoroughly understand the Java memory model, why does it cause thread safety issues [summary of vomiting blood]

Recently, some enthusiastic citizens have questioned the "Java memory model": whether the thread will load all the data that needs to be operated into the memory
insert image description here
According to "I Am a Silly Bag", it can be seen that the party involved Dandan (pseudonym) is currently in a stable mood, and it seems realized the problem

Yes, the smart balls have found the answer (the answer will come later)


After the incident, the big guys in the group attached great importance to it, immediately held an online meeting, and established a Java memory model expert group to respond, requiring the organization to quickly and properly handle it, quickly find out the root cause of the problem, immediately organize online Q&A, and provide further guidance Work to prevent the same problem from recurring and prevent brothers from getting their favorite offers

When I think that many of my friends haven't understood the Java memory model, I can't eat enough to sleep, and I'm indifferent even when I see the black silk.

then

It took a few days and a few more hairs to try to help you understand a wave~

Regarding the Java memory model, we can talk a lot and talk a lot, but don’t panic, let’s sort out the questions first:

  • What is the Java Memory Model?
  • Why is there a Java memory model?
  • What problem does the Java memory model raise?
  • Will the thread load all the data it needs to operate on into memory?

As stated by the parties:

When a thread operates data, it will copy a copy of the data from the main memory to its own working memory, and then write it back to the main memory after the operation. If the data is too large, will it also be copied to the working memory?

To understand this problem, we must first study what is the Java memory model


Many students will confuse the Java memory model with the JVM memory model , which are two completely different things

Java Memory Model : The full name is Java Memory Model, or JMM for short, which is a virtual machine specification, which will be discussed in detail below;
JVM memory model : The full name is Java Virtual Machine, or JVM for short, which is also a virtual machine specification, and this article will not talk about jvm;

If you want to develop a virtual machine that can run Java programs, you must follow these two specifications (of course, there are far more specifications to follow). Only in this way can Java programs run happily on your virtual machine. , our most common hotspot vm follows these specifications;

The origin of the Java memory model

insert image description here

It 's a long story,
insert image description here
I'll keep it short

Origin of the problem

This involves the development history of CPU manufacturers and memory manufacturers. . .

We chicken road, when the cpu executes instructions, it often needs to operate the data in the memory

In order to facilitate understanding, let me give a chestnut, take i = i + 1

The cpu first reads the current value of i from the memory, adds +1, and then writes the calculation result back to the memory
insert image description here

Everything was fine at first, but with the development of technology, the CPU execution efficiency far exceeds the memory read and write efficiency, so there is a phenomenon

The CPU takes a very short time to perform the +1 operation, assuming that it only takes 1ms, and reading i from the memory and writing it back to the memory takes a long time, assuming it is 10ms

The cpu obviously only needs 1ms, and it is dragged to 11ms by the memory. How can it stand it?

So, the witty cpu factory thought of a way

Solution

This method is also mentioned in the book "In -depth Understanding of the Java Virtual Machine "
insert image description here

Simply put, a layer of cache , which is what we usually call the L1, L2, and L3 caches. This cache is generally small, but it's fast, you know what I mean?

Note: The knowledge point is coming, be sure to separate the cpu cache from the memory of the memory stick

This is the memory of the memory stick (can be viewed in the system properties)
insert image description here

This is the cpu cache (task manager - performance column can be viewed)
insert image description here

So now the operation flow becomes:

The cpu will copy the data to be used from the main memory to the cache in advance. When the cpu performs the calculation operation, it will search from the L1, L2, and L3 caches in turn. If there is needed data, operate directly and the calculation is over. Then flush to the main memory; if not, go to the main memory to find

insert image description here

The problem of CPU being pulled by memory inefficiently has been solved

A long time has passed. . .

CPU manufacturers introduced multi-core processors, which led to another problem: thread safety

Each core of a multi-core processor has its own cache (each cpu architecture is different, it depends on how the cpu manufacturer does it, the current cpus on the market are generally L1, L2 independent, L3 shared)

It can be seen from the above that the L1 cache of my cpu is 384k. This 384k is not shared by six cores, but 6 * 32 * 2, as shown below
insert image description here

Now, the architecture has become
insert image description here
(this diagram is a simplified version, the actual architecture diagram is much more complicated than this, I am lazy to draw those details)

So, now the question arises, what happens if threads on different cores operate on the same data at the same time?

Let's assume

Core a has a thread t1, and core b has a thread t2
. Before the calculation starts, the value of i in the memory is 0, and the value of i in the cache corresponding to the two threads is also 0.
At a certain moment, the two threads simultaneously execute i + 1
t1 After executing i = 1, write it back to memory. At this time, the value of i in memory has changed from 0 to 1.
t2 After executing i = 1, it also writes i = 1 back to memory. This is The new i value written back by t1 is overwritten

Originally i should be equal to 2 after +1 twice, but the actual result is equal to 1, understand what I mean, most data exception problems in concurrent programming come from this

Therefore, in concurrent programming, as long as the write operation is involved, we should ensure synchronization to obtain reliable final data

Here, we can summarize what memory model

What is the Java Memory Model

As can be seen from the above architecture diagram, threads need

As mentioned above, the Java memory model is a protocol; if a thread wants to operate data, it needs to read from the main memory to the working memory, and then write it back to the main memory after the operation. It seems simple, but there are many underlying technical details in between. , such as:
when to read?
When will it be written?
How to allocate when multiple threads read and write together?
So the question is, the CPU and memory on a server may be provided by different manufacturers. If the underlying implementation details of them do not match, how can the program run normally? It is impossible to hold a meeting with all manufacturers every time a product is designed. Therefore, for the sake of convenience and unity, there is the Java memory model, which is used to standardize the underlying implementation of memory reading and writing for different hardware and operating systems. Differences ;
only by masking these differences can Java compile once, run anywhere


Back to the original starting point, your green face in memory~
Now the answer is announced
insert image description here

Speaking of which, let's talk about the lower-level cold knowledge of CPU

instruction rearrangement

In concurrent programming, in addition to the thread safety issues brought about by the Java memory model, the CPU and virtual machines themselves also have similar issues

  • About cpu: In order to utilize the cpu from the points, optimization will be done when the instruction is actually executed
  • About virtual machine: In HotSpot vm, in order to improve execution efficiency, JIT (just-in-time compilation) mode will also perform instruction optimization

Instruction rearrangement can indeed improve efficiency in most scenarios, but some operations are strongly dependent on the order of code execution. At this time, we need to turn off instruction rearrangement. I believe many friends have already guessed it.

That's right, it's volatile

Regarding volatile, if you want to fully understand it, you have to talk a lot, so I won’t talk about it here, I will write a separate article another day.

Give an example to illustrate what instruction rearrangement is and how to prevent it:
insert image description here
This pseudo code is taken from "In-depth Understanding of Java Virtual Machine":
the scenario described in it is a common configuration reading process in development, but we generally do not process configuration files. Concurrency occurs, so I didn't realize it would be a problem.
Just imagine, if the volatile modification is not used when defining the initialized variable, the last code "initialized=true" in thread A may be executed in advance due to the optimization of instruction reordering (although Java is used as pseudo code here, all the Refers to the reordering optimization is a machine-level optimization operation, and the early execution means that the assembly code corresponding to this statement is executed in advance), so that the code using the configuration information in thread B may have errors, and the volatile keyword can be avoided. the occurrence of such situations


ok i'm done

Guess you like

Origin blog.csdn.net/qq_33709582/article/details/122361362