Click “like” to see again, form a habit, wechat search [Three prince Aobing] to pay attention to the Internet tools of survival.

This article has been included in GitHub github.com/JavaFamily, there are a line of large factory interview complete test sites, information and my series of articles.

Volatile may be a must-ask topic in the interview. Many of our friends only know how to use Volatile. Today, let’s take a look at it from a different Angle.

First follow c c to see a demo code


If the thread changes the flag variable, the main thread will be able to access it.

Why does this happen? There’s another thing we need to talk about.

The JMM (JavaMemoryModel)

JMM: The Java Memory model is defined in the Java Virtual Machine specification. The Java memory model is standardized and does not distinguish between underlying computers.

Before we get down to business, let’s give a brief introduction to the memory model of modern computers.

The memory model of modern computers

In fact, the speed of CPU and memory in early computers was about the same, but in modern computers, the speed of CPU instruction is far faster than the speed of memory access. Because of the difference of several orders of magnitude between the computing speed of the computer’s storage device and that of the processor, So modern computer systems have to add a buffer between memory and the processor, a Cache that reads and writes as fast as possible as the processor can run.

Copying the data needed for an operation to the cache allows the operation to proceed quickly. When the operation is complete, it is synchronized from the cache back to memory so that the processor does not have to wait for slow memory reads and writes.

Cache-based storage interaction is a good solution to the speed contradiction between processor and memory, but it also introduces higher complexity to computer systems because it introduces a new problem: CacheCoherence.

In a multiprocessor system, each processor has its own cache, and they share the same MainMemory.


Then we can talk about JMM.

JMM

The JavaMemoryModel describes the rules for accessing variables (shared by threads) in a Java program, as well as the low-level details of storing variables in the JVM, storing them into memory, and reading variables from memory.

The JMM has the following provisions:

All shared variables are stored in main memory, by which I mean instance variables and class variables, excluding local variables, which are thread private and therefore do not have contention issues.

Each thread also has its own working memory, which keeps working copies of variables used by the thread.

All operations on variables must be performed by the thread in working memory, rather than directly reading or writing variables in main memory.

Different threads cannot directly access the variables in each other’s working memory, and the values of variables between threads need to be passed through the main memory.

Relationship between local memory and main memory:


It is this mechanism that leads to the visibility problem, so let’s talk about the visibility solution.

Visibility solutions

lock


Why does locking solve the visibility problem?

Before and after a thread enters the synchronized code block, the thread will acquire the lock, clear the working memory, copy the latest value of the shared variable from the main memory to the working memory, execute the code, refresh the value of the modified copy back to the main memory, and release the lock.

A thread that does not acquire the lock blocks the wait, so the value of the variable must always be up to date.

Volatile modifies shared variables


The initial code should look like this when optimized:


What did Volatile do?

Each thread reads data from main memory into its own working memory as it manipulates data. If it does so and fails to write, copies of variables of other threads that have already read will be invalidated and will have to read data from main memory again.

Volatile ensures the visibility of shared variables performed by different threads. That is, when a volatile variable is modified by one thread, the other thread immediately sees the latest value when the change is written back to main memory.

It may seem easy to add a keyword, but in fact he has worked very hard behind the scenes. Let me explain the meaning of these terms in terms of the cache consistency protocol at the computer level.


We mentioned earlier that when multiple processors work on the same main memory area, it can lead to inconsistent cache data, as an example of variable sharing between multiple cpus.

If this happens, whose cached data will be used when synchronizing back to main memory?

To solve the consistency problem, each processor needs to follow some protocols when accessing the cache and operate according to the protocols. These protocols include MSI, MESI (IllinoisProtocol), MOSI, Synapse, Firefly, and DragonProtocol.

Let’s talk about Intel’s MESI

MESI (Cache Consistency Protocol)

When a CPU writes data, if it finds that the variable is a shared variable, that is, a copy of the variable exists in other cpus, it sends a signal to inform other cpus to set the cache line of the variable to invalid state. Therefore, when other cpus need to read the variable, they find that the cache line of the variable is invalid. Then it will re-read from memory.

How do you find out if the data is invalid?

sniffer

Each processor by sniffing the spread of the data on the bus to check the value of the cache is expired, when the processor found himself cache line corresponding to the memory address has been changed, and will be set for the current processor cache line in invalid state, when the processor to modify the data operation, will start from system memory read data to the processor cache.


I don’t know if you’ve noticed the downside of sniffing?

Bus storm

Due to Volatile’s MESI cache consistency protocol, which required constant sniffing and cas loops from main memory, the bus bandwidth could peak due to invalid interactions.

So don’t use Volatile a lot, and the timing of using Volatile and locking depends on the situation.

Let’s talk about instruction reordering again

Disallow instruction reordering

What is reordering?

To improve performance, compilers and processors often reorder instructions from a given code execution order.

What are the types of reordering? How will the source code be reordered to final execution?


A good memory model actually loosens the rules of the processor and compiler, which means that both software and hardware technologies are fighting for the same goal: to make the execution as efficient as possible without changing the results of the program.

The JMM minimizes constraints on the bottom layer so that it can play to its strengths.

Therefore, when executing a program, the compiler and processor often reorder instructions to improve performance.

General reordering can be divided into the following three types:

  • Compiler optimized reordering. The compiler can rearrange the execution order of statements without changing the semantics of single-threaded programs.

  • Instruction – level parallel reordering. Modern processors use instruction-level parallelism to superimpose multiple instructions. If there is no data dependency, the processor can change the execution order of the corresponding machine instructions.

  • Memory system reordering. Because the processor uses caching and read/write buffers, this makes it appear that load and store operations may be performed out of order.

As-if-serial is a serial serial.

as-if-serial

No matter how reordered, the results of a single thread cannot be changed.

The compiler, runtime, and processor must comply with the AS-IF-Serial semantics.

So how does Volatile guarantee against reordering?

The memory barrier

The Java compiler inserts memory barrier instructions in place to prohibit reordering of a particular type of handler when generating a sequence of instructions.

To implement the memory semantics of volatile, the JMM restricts certain types of compiler and processor reordering. The JMM makes tables for volatile reordering rules for compilers:


Note that volatile writes insert memory barriers at the front and at the back, whereas volatile reads insert two barriers at the back.

write


read


In order to speed up the process, the JVM optimizes code for compilation, also known as instruction reorder optimization. Instruction reorder in concurrent programming has some security risks, such as invisibility between multiple thread operations caused by instruction reorder.

If the programmer has to understand these underlying implementation and specific rules, then the programmer burden is too heavy, seriously affect the efficiency of concurrent programming.

Starting with JDK5, the concept of happens-before was introduced to illustrate memory visibility between operations.

happens-before

If the results of one operation need to be visible to another, there must be a happens-before relationship between the two operations.

Volatile field rule: Writes to a volatile field are happens-before any subsequent reads to that volatile field by any thread.

If now I’ve changed falg to false, then the next operation, you have to know that I’ve changed.

So with all this talk, it’s important to know that atomicity is not guaranteed by Volatile, that atomicity is guaranteed by Volatile, and that other methods can be used.

There is no guarantee of atomicity

It’s an operation that either succeeds completely or fails completely.

If I have N threads adding up the same variable, there’s no way I’m going to get it right, because reading and writing is not atomic.

The solution is as simple as using Atomic classes, such as AtomicInteger, or locking (remember to focus on the underlying layer of Atomic).

application


There are eight ways to write singletons, and I’ll mention the one in particular that involves Volatile.

Why double check, you might wonder? What if they didn’t use Volatile?

I’ll start with the benefits of disallowing instruction reordering.

Object To actually create an object, go through the following steps:

  • Allocate memory space.
  • Invoke the constructor to initialize the instance.
  • Return the address to the reference

As I mentioned above, it is possible for instructions to be reordered. It is possible for the constructor to assign a value before the object is initialized, create a memory area and return a reference to memory before the object is actually initialized.

But the other thread decides instance! < span style = “max-width: 100%; clear: both;

How is visibility guaranteed?

Because of the visibility, thread A initializes objects in its own memory before writing back to main memory, and thread B does the same, so it creates multiple objects, not really A singleton.

As mentioned above, volatile and synchronized are different.

The difference between volatile and synchronized

Volatile modifies only instance and class variables, while synchronized modifies methods, as well as code blocks.

Volatile ensures visibility of data, but not atomicity (multithreaded writes are not thread-safe). Synchronized is an exclusive mechanism. Volatile is used to disallow instruction reordering: it addresses out-of-order execution of singleton double-checked object initialization code.

Volatile can be considered a lightweight version of synchronized. Volatile does not guarantee atomicity, but if multiple threads of assignment to a shared variable are performed and nothing else is performed, volatile can be used instead of synchronized because assignment is inherently atomic. Volatile guarantees visibility, and therefore thread safety.


conclusion

  1. The volatile modifier is used when an attribute is shared by multiple threads, and one thread modifiers the attribute, such as BooleanFlag, and the other threads immediately obtain the modified value. Or as a trigger for lightweight synchronization.
  2. Volatile, whose reads and writes are lockless, is not a substitute for synchronized because it provides no atomicity or mutual exclusion. Because there is no lock, there is no time to acquire and release the lock, so it is low-cost.
  3. Volatile only applies to attributes, and we use volatile to modify attributes so that compilers don’t reorder the instructions for those attributes.
  4. Volatile provides visibility. Changes made by any thread are immediately visible to other threads. Volatile properties are never cached by threads and are always read from main memory.
  5. Volatile provides a happens-before guarantee that writes to volatile v are happens-before all subsequent reads to V by other threads.
  6. Volatile makes the assignment of long and double atomic.
  7. Volatile ensures security by enabling visibility and disallowing instruction reordering in singleton double-checks.

Note: I think Volatile would be a plus for interviewers if they knew all of the above, but I haven’t covered much of the basics of computer memory, so you’ll need to learn more later. If you can, you can wait until I write the basics of computer.

omg

img

Due to the update of articles and videos, Bing Bing has not had a rest on weekends for more than half a year. He has been rushing at his desk in the company, always wanting to find time to go out to play. Thinking that one day of annual leave is useless, he asked for two days to go out to play.

So 51 can come back early, ready to restore the update of the video, when you are watching, Ao Third should be on the train of travel, yes I carried this bag, to finish writing, I have not determined where to go, I wish you a happy holiday in advance.

I am Aobing, a tool to survive on the Internet.

The more you know, the more you don’t know, the talent of the [three] is the biggest impetus for the creation of c c, we will see you next time!

Note: If there are any mistakes or suggestions in this blog, please leave a comment.


[Information] [interview] [resume] I prepared the interview materials and resume template, this article GitHub github.com/JavaFamily has been included, there are big factory interview complete test site, welcome Star.