Volatile of JUC

I have been reading the book "The Art of Java Concurrent Programming" recently. After reading it, I have a feeling that most of the articles and sources of information about JVM and JUC on the Internet come from this book and "In-depth Understanding of Java Virtual Machine". As a Javaer, I should read these two books a few times and take more notes. Let's start~

First look at a piece of code:

public class TestVolatile {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo, "Thread-A").start();

        while (true) {
            if (threadDemo.isFlag()) {
                System.out.println(Thread.currentThread().getName() + "=============");
                break;
            }
        }
    }

}

class ThreadDemo implements Runnable {

    private boolean flag = false;

    @Override
    public void run() {

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = true;

        System.out.println("flag = " + isFlag());
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

What is the output? Why?

Enter image description

It is found that the value of flag has been changed to true, but the while loop in the Main thread has not ended. What is the reason?

This problem is called the memory visibility problem , that is, when multiple threads operate on shared data, the threads cannot see the modified shared variables between them.

value, the reason for the appearance is related to the JVM memory model (JMM):

In Java, all shared variables (instance fields, static fields, and arrays) are stored in heap memory. Because heap memory is shared between threads, these variables are called shared variables. JVM defines threads and main memory ( This can be understood as an abstract relationship between the heap memory): the shared variables between threads are stored in the main memory, and each thread has a private local working memory (which can also be understood as a cache, mainly to solve efficiency issues), a copy of the thread to read/write shared variables is stored in local memory

Regarding the CPU operation of main memory and local working memory, the relationship between CPU, cache and memory can be compared by analogy.

Enter image description

To communicate between thread A and thread B (to let each other know after changing the value of the shared variable), they must go through the following two steps:

  • Thread A refreshes the updated shared variable in local working memory A to main memory
  • Thread B goes to main memory to read the shared variable that thread A has updated before

Analysis of the causes of the above problems:

  1. At the beginning, there is a shared variable copy value flag = false between the thread Thread-A and the main thread
  2. The thread Thread-A changes the local working memory flag value to flag = true, and it will not be updated to the main memory immediately at this time.
  3. When main is running, it always uses the flag = false obtained from the main memory at the beginning and saved in the local working memory, so the while loop has never been able to break

Solution:

Java provides the volatile keyword. If a field is declared volatile, the Java thread memory model ensures that all threads see the same value of the variable.

When a shared variable modified by a volatile variable is written, when the JVM executes the bytecode and converts it into an assembly instruction code, there will be an extra line of assembly code prefixed with lock, and the instruction prefixed with Lock will cause two problems under multi- and multi-core processors. matter:

  1. Write the data of the current processor cache line (local working memory) back to the system memory (main memory) Corresponding: When writing a volatile variable, JMM will refresh the shared variable value in the local working memory corresponding to the thread to the main memory middle.

  2. This write back to memory invalidates the data cached at that memory address in other CPUs: when reading a volatile variable, the JMM invalidates the thread's local memory. The thread will next read the shared variable from the main.

volatile has many properties:

  1. visibility. A read of a volatile variable always sees (any thread) the last write to the volatile variable. What's the meaning? That is, when a thread modifies the value of a variable, the new value is immediately synchronized to main memory. When other threads read this variable, they will also pull the latest variable value from main memory.

  2. atomicity. Reads and writes to any single volatile variable are atomic, but compound operations like volatile++ are not atomic

Why can volatile have such a feature? (Let’s get a general understanding first, and then we may learn and record the happens-before principle in detail later)

The happens-before principle (happens-before), starting from JDK1.5, Java uses the new JSR-133 memory model, and uses the happens-before principle to describe the visibility between threads. In JMM, if the result of an operation execution needs to be visible to another operation, there must be a happens-before relationship between these two operations

The happens-before rules that are closely related to programmers are as follows:

  • Program order rules: every operation in a thread happens-before any subsequent operations in that thread.
  • Monitor rule: unlocking a lock happens-before subsequent locking of the lock.
  • Volatile variable rule: Writes to a volatile field occur-before any subsequent reads of the volatile field.
  • Transitive: If A happens-before B, and B happens-before C, then A happens-before C.

After understanding the above knowledge points, go back to the original question, modify the flag with volatile and then run the verification to draw a conclusion

public class TestVolatile {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo, "Thread-A").start();

        while (true) {
            if (threadDemo.isFlag()) {
                System.out.println(Thread.currentThread().getName() + "=============");
                break;
            }
        }
    }

}

class ThreadDemo implements Runnable {

    private volatile boolean flag = false;

    @Override
    public void run() {

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = true;

        System.out.println("flag = " + isFlag());
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

Enter image description

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325354573&siteId=291194637