(1) The volatile keyword of JUC

 

Concurrency and multi-threading have been used in recent projects. I have known about it before, but I have not studied it completely. I will take this opportunity to learn a complete summary of the java.util.concurrent package.
To study the juc package, you must first study the volatile keyword and the cas algorithm, because volatile and cas are used in many places in the juc package. Today, I will write volatile first.
The volatile keyword has been touched more or less before. Before, I only understood that it was to solve the problem of memory visibility between multiple threads. It was only used, and the principle was not investigated.
If we have such a requirement, the program has two threads, and the two threads share a switch parameter. When thread A turns off the switch, thread B ends execution, otherwise thread B keeps running.
Write the test program as follows:
public class VolatileTest {
    //Here the main method represents thread B
    public static void main(String args[]) {
        ThreadA threadA = new ThreadA();
        new Thread(threadA).start();

        while (true) {
            if(!threadA.isFlag()) {
                System.out.println("Switch closed, exit...");
                break;
            }
        }
    }
}

class ThreadA implements Runnable {

    private boolean flag = true;

    @Override
    public void run() {
        try {
            Thread.sleep(2000); //Sleep for two seconds
        } catch (InterruptedException e) {
        }
        flag = false;
        System.out.println("Thread A is executing...");
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
 
Our ideal running result should be:
Thread A is executing...
Switch is off, exit...
  But the actual result is indeed:
Thread A is executing...
 Then the main thread has been in the while loop. According to the normal idea, we have modified the value of flag to false in thread A during the loop execution of thread B ( Main thread), but thread B (Main thread) has been looping without reaching the exit. The condition, that is, thread B obtains the value of threadA.isFlag() is always true, is it strange, but why is this, let's take a look at the reason.
When the program is executed, all instructions are executed in the CPU, including the reading and writing of data. Since the temporary data during the running of the program is stored in the main memory (physical memory), there is a The problem is that because the CPU executes very fast, the process of reading data from memory and writing data to memory is much slower than the speed of CPU executing instructions. Therefore, if the operation of data at any time has to pass through and the memory Interactively, it will greatly reduce the speed of instruction execution. So there is a cache in the CPU. That is, when the program is running, it will copy the data needed for the operation from the main memory to the cache of the CPU, then the CPU can directly read data from its cache and write to it when performing calculations. After the operation is completed, the data in the cache is flushed to the main memory.
Combining the above, let's take a look at our program again. First, we declare flag = true in the main memory. At this time, thread B (Main thread) gets the value of flag=true from the main memory and puts it in the cache, and thread A gets the value of flag=true from the main memory. Get flag=true and put it in the cache, then modify flag=flase, update the value of the main memory flag = false, but the flag=true has been cached in thread B, and the value of the main memory has not been synchronized, so we have Often said memory visibility problem.
Then we combine the knowledge we have learned before to solve this problem:
while (true) {
            synchronized (threadA) {
                if(!threadA.isFlag()) {
                    System.out.println("Switch closed, exit...");
                    break;
                }
            }
        }
  Add synchronized to thread A when the main thread executes, and the execution result is consistent with the expected value (Lock can also be implemented):
Thread A is executing...
Switch is off, exit...
 But everyone who knows synchronized should know that synchronized is a mutual exclusion lock, that is, thread B must wait for thread A to complete execution before continuing to execute. In order to solve the problem of memory visibility, java provides the volatile keyword to ensure visibility. We Modify the program and look at the execution result, and modify it as follows:
private volatile boolean flag = true;
 Declare falg as a volatile variable, and delete the synchronization lock (synchronized) added in the previous step. The execution result is as follows:
Thread A is executing...
Switch is off, exit...
 But why modifying variables with volatile can ensure the visibility of memory, mainly in the following points:
 First: using the volatile keyword forces the modified value to be written to main memory immediately;
 Second: If the volatile keyword is used, when thread A is modified, the cache line of the cache variable flag in the working memory of thread B will be invalid (if it is reflected to the hardware layer, it is the corresponding cache line in the L1 or L2 cache of the CPU). invalid);
 Third: Since the cache line of the cache variable flag in the working memory of thread B is invalid, thread B will go to the main memory to read the value of the variable flag again.
Can volatile replace synchronized? I'll write this question next time.
 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326926450&siteId=291194637