当前位置:网站首页>Learn volatile, you change your mind, I see

Learn volatile, you change your mind, I see

2020-11-08 16:59:00 How sensible a man is

More wonderful articles , Please pay attention to xhJaver, JD engineers grow up with you

volatile brief introduction

Commonly used to modify shared variables , Ensure visibility and prohibit command reordering

  • When multithreading operates on the same variable , A thread has finished modifying , Other threads can immediately see the modified value , Guaranteed to share variables visibility
  • Prohibit command rearrangement , Ensure that the code is executed Orderliness
  • There is no guarantee of atomicity , For example, common i++

    ( But it's atomic for a single read or write )

Visibility code example

The following code suggests using PC Check it out , Copy paste runs directly , There are detailed notes

Let's write a code test , When multithreading modifies shared variables, do you need to use volatile Modifying variables

  1. First , We create a task class
 public class Task implements Runnable{
 @Override
 public void run() {
 System.out.println(" This is a "+Thread.currentThread().getName()+" Thread start ,flag yes  "+Demo.flag);
 // When the shared variable is true when , It's stuck here all the time , Don't output the following sentence 
 //  When flag yes false when , Output the following sentence 
 while (Demo.flag){
 }
 System.out.println(" This is a "+Thread.currentThread().getName()+" Thread end ,flag yes  "+Demo.flag);
 }
} 

2. secondly , Let's create a test class

class Demo {
 // Shared variables , Not yet. volatile modification 
 public static   boolean flag = true ;
 public static void main(String[] args) throws InterruptedException {
 System.out.println(" This is a "+Thread.currentThread().getName()+" Thread start ,flag yes  "+flag);
 // Open the thread just now 
 new Thread(new Task()).start();
 try {
 // Sleep for a second , Make sure that the thread just now ran to while loop 
 // Or it hasn't run to while loop , The main thread will flag Turn into false
 Thread.sleep(1000L);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 // Changing shared variables flag To false
 flag = false;
 System.out.println(" This is a "+Thread.currentThread().getName()+" Thread end ,flag yes  "+flag);
 }
}

3. Let's look at the output

so , The program doesn't end , He's stuck here , Why is it stuck here , It's because we changed the shared variables in the main thread flag by false, But another thread didn't perceive , Changes to this variable are not visible to another thread

  • If you use volatile Variable modification , It turns out to be like this

public static volatile boolean flag = true

so , This time, the variables modified by the main thread are perceived by another thread , Guarantee the variable's visibility

Visibility principle analysis

that , magical volatile What the hell is going on at the bottom , Your change , He can't escape his eye ? Why don't you use him to modify variables , The change of variables is invisible to other threads ?

Answer this question first , We need to know JMM(Java Memory model )

notes : Local memory is JMM An abstraction of , It's not real , Local memory it covers caching , Write buffer , After that, the location of the compiler and other hardware registers is optimized
  • So we can analyze , The main thread modified the variable , But other threads don't know , There are two situations

    1. The variables modified by the main thread have not been flushed to the main memory yet , The other thread reads the previous variable
    2. The variables modified by the main thread are flushed to the main memory , But other threads are reading local copies
  • When we use volatile Keyword modification of shared variables can do the following two things

    1. When a thread modifies a variable , Will force refresh to the main memory
    2. When a thread reads a variable , It forces the variables to be read from main memory and flushed to working memory

Command rearrangement

  • What is instruction rearrangement ?

In order to improve the efficiency of the program , The compiler and cpu It rearranges the order in which the code is executed , But sometimes it brings a lot of problems

Let's look at the code

// Instruction rearrangement test 
public class Demo2 {
private Integer number = 10;
private boolean flag = false;
private Integer result = 0;
public void  write(){
this.flag = true; // L1
this.number = 20; // L2
}
public void  reader(){
while (this.flag){ // L3
this.result = this.number + 1; // L4
}
}
}

If we have A、B Two threads They execute separately write() Methods and reader() Method , The order of execution may be as shown in the following figure

  • Problem analysis : As shown in figure shows ,A Thread L2 and L1 Has been reordered , If it does, then , When A After execution L2 when ,B Start execution L3, But this time flag Still for false, that L4 It can't be executed , therefore result The value of is still the initial value 0, Not changed to 21, Cause program execution error

This is the time , We can use it volatile Keyword to solve this problem , It's simple , just

private volatile Integer number = 10;

  • This is the time L1 It must be L2 Go ahead
A The thread is modifying number Variable is 20 When , Just make sure that the code in front of this code must be executed before this line of code , stay number It's inserted at Memory barrier , In order to achieve volatile Memory semantics of , When the compiler generates bytecode , Memory barriers are inserted into the instruction sequence to prevent certain types of processor rearrangements

Memory barrier

What is the memory barrier ? There are four types of memory barriers in total , They are

  1. LoadLoad barrier :

    • Load1 LoadLoad Load2 Make sure Load1 The loading of data for is prior to Load2 And all subsequent loading instructions loading
  2. LoadStore barrier :

    • Load1 LoadStore Store2 Make sure Load1 The loading of data for is prior to Store2 And all subsequent storage instructions storage
  3. StoreLoad barrier :

    • Store1 StoreLoad Load2 Make sure Store1 The data of is visible to other processors ( Refresh to memory ) Precede Load2 And all subsequent loading instructions loading
  4. StoreStore barrier :

    • Store1 StoreStore Store2 Make sure Store1 Data is visible to other processors ( Refresh to memory ) Precede Store2 And all subsequent storage instructions storage
> StoreLoad  It's an all powerful barrier , At the same time, it has other 3 The effect of a barrier . The cost of executing the barrier is more expensive , Because the processor usually flushes the contents of the current write buffer to memory (Buffer Fully Flush)

  • load load It's reading int a = load1 ( load1 The loading of )
  • Storage store Is to write store1 = 5 ( store1 The storage )

volatile With the memory barrier

that volatile What does it have to do with these four memory barriers , How to insert it ?

  1. volatile Write ( Insert barriers in front and back )

    • Insert a before StoreStore barrier
    • Insert a StoreLoad barrier
  2. volatile read ( Just insert a barrier in the back )

    • Insert a LoadLoad barrier
    • Insert a LoadStore barrier

The official form is like this

We're looking back at our program

this.flag = true; // L1
this.number = 20; // L2

because number By volatile Modify the ,L2 This sentence is volatile Write , This is what the barrier should look like

this.flag = true; // L1
//  StoreStore   Make sure flag Data is visible to other processors ( Refresh to memory ) Precede number And all subsequent storage instructions storage 
this.number = 20; // L2
// StoreLoad   Make sure number Data is visible to other processors ( Refresh to memory ) Load before all subsequent storage instructions 

therefore L1,L2 The execution order of is not reordered

ps: The fourth building of headquarters is getting better and better , Reward yourself with a cup of milk tea

More exciting , Please pay attention to the official account. xhJaver, JD engineers grow up with you

Past highlights

版权声明
本文为[How sensible a man is]所创,转载请带上原文链接,感谢