Do you really understand Volatile?

What is volatile?

Sec understand Wikipedia: volatile is a feature modifier (type specifier)

Speaking of volatile, many people know that its function has two functions: visibility and prohibiting instruction reordering. Then if you ask you again, how does it reflect visibility, how to prohibit instruction reordering, and where it is used in Java How did they achieve it?

Obviously these questions are quite difficult. If faced with such a problem, we'd better explain briefly the role of volatile first, and then directly use the relevant knowledge he knows in Java. The knowledge here is limited, so I am Give me what I know, hope it helps!

First of all:

Visibility:

A thread modifies the value of a shared variable, other threads can also see the latest modified value

Reordering is prohibited:

Compilers and processors usually reorder instructions (how much instruction is not introduced), which can cause problems in some thread concurrency scenarios, and prohibiting reordering can control the occurrence of these problems.

The following two examples explain the realization of these two features:

1. Visibility

Prerequisite understanding:

The sequence of thread operations on shared variables in main memory is: first copy a copy of the variable from main memory to its own working memory, modify it, and then update the modified value to main memory

Scenes:

Suppose there are two threads A and B, and they all operate on a shared variable Number = 10 in the main memory. Then they will first copy a copy from the main memory to their respective working memory. Suppose thread A is artificially After sleeping for 3s, execute a method to change Number = 10—modify—>Number =1024, and update it to the main memory, and thread B will have a spinning While at the beginning, and it will always spin if it is used to determine Number = 10. If not equal to 10, continue to execute

Example code:

package com.xiaozhao.juc;

import java.util.concurrent.TimeUnit;

/**
 * @author : Carson-Zhao
 * @date : 2020/7/12 18:00
 *
 *
 * JMM可见性
 *
 * 如果没有volatile修饰,通知main线程,程序则会在上面的While中一直循环,
 * 因为线程AAA修改了Number的值,但没有任何东西通知main
 *
 */
public class JMMDemo {
    public static void main(String[] args) throws InterruptedException {
        Data data = new Data();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                data.add();
                System.out.println(Thread.currentThread().getName()+"====>"+data.Number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"AAA").start();

        while (data.Number == 10){
            //3s后需要一种通知机制告诉main线程,Number已修改为1024,请跳出While—————应该做事儿
        }
        System.out.println(Thread.currentThread().getName()+"====>"+data.Number);
    }
}

class Data{

    int Number = 10;
    public void add(){

        this.Number = 1024;

    }
}

Output when volatile is not used:

The thread B will always spin, the thread will not stop, because the Number is still 10 in its working memory, and no one has notified it that the A thread has changed the Number to 1024.

Output when using volatile:

Volatile to notify the B thread that the Number value has been modified. At this time, while is not established, it jumps out of the loop and executes the following code normally

to sum up:

It can be said that volatile serves as a notification function. It can be used to inform other threads that all its modified variables are uncertain and variable. If it needs to be acquired, it must be acquired from the main memory every time to ensure The visibility of the variable in memory!

 

2. No reordering

A simple example:

package com.xiaozhao.juc;

/**
 * @author : Carson-Zhao
 * @date : 2020/7/14 13:38
 */
public class VolatileDemo {
    public static void main(String[] args) {
        Object s = new Object();
    }
}

After decompilation:

D:\Environment\Java\jdk1.8.0_251\bin\javap.exe -c com.xiaozhao.juc.VolatileDemo
Compiled from "VolatileDemo.java"
public class com.xiaozhao.juc.VolatileDemo {
  public com.xiaozhao.juc.VolatileDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/Object
       3: dup
       4: invokespecial #1                  // Method java/lang/Object."<init>":()V
       7: astore_1
       8: return
}

Process finished with exit code 0

We locate in the main method. When (left) 0, we perform a new operation through the instruction, but the new here is at least a semi-initialized operation. Assuming that we swap instructions 4 and 7 at this time, this is Java Reordering in. Then at this time, the index is established before the second initialization, and the S object points to a semi-initialized object. Suppose we have another thread to judge S. If it is empty, it will be recreated (a series of operations). If it is not empty, it will be used directly. In the previous scenario, the second thread will directly think that s is right or wrong. An empty object, then he will take S to use, but at this time he got a semi-initialized object. Obviously, this design is unreasonable, then our volatile role is reflected at this time !

It will prohibit the exchange of instructions 4 and 7 (reordering is prohibited) to ensure that the two initialization processes of the S object are complete, so as to ensure that when other threads come to use S, they get a complete object

use:

       The volatile keyword is used in many places, such as modified State in AQS, double double check singleton mode, and some write operations to variables that do not depend on the current value and the variable is not included in an invariant with other variables.

At last:

       Thanks for joining us! Learn because of the dishes, and encourage each other!

Guess you like

Origin blog.csdn.net/weixin_43562937/article/details/107335805