The JUC series of Java interviews: talk about the understanding of Volatile

  • JUC (java.util.concurrent)
    • Processes and threads
      • Process: A program running in the background (a software we open is a process)
      • Thread: a lightweight process, and a process contains multiple threads (in the same software, running windows at the same time is a thread)
    • Concurrency and parallelism
      • Concurrency: access to something at the same time is concurrency
      • Parallel: Doing something together is parallel
  • Three packages under the JUC
    • java.util.concurrent
      • java.util.concurrent.atomic
      • java.util.concurrent.locks

Talk about the understanding of Volatile

Volatile is not applicable in the daily single-threaded environment

  • Volatile is 轻量级a synchronization mechanism (three features) provided by the Java virtual machine
    • Ensure visibility
    • No guarantee of atomicity
    • Prohibit order rearrangement

What is JMM

JMM is the Java Memory Model, also known as Java Memory Model, JMM for short. It is an abstract concept in itself and does not actually exist. It describes a set of rules or specifications through which each variable in the program is defined ( Including instance fields, static fields and elements that make up the array object) access methods

JMM regulations on synchronization:

  • Before the thread is unlocked, the value of the shared variable must be flushed back to the main memory
  • Before the thread is unlocked, it must read the latest value of the main memory to its own working memory
  • Locking and unlocking are the same lock

Since the entity of the JVM running program is a thread, and when each thread is created, the JVM will create a working memory (called stack space in some places) for it. The working memory is the private data area of ​​each thread, and the Java memory model stipulates all Variables are stored in the main memory. The main memory is a shared memory area, which can be accessed by all threads. You can 但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写会主内存not directly manipulate the variables in the main memory. The working memory of each thread stores a copy of the variable in the main memory, so it is different The working memory of each other cannot be accessed between threads. The communication (passing value) between threads must be completed through the main memory. The brief access process is as follows:

image-20200309153225758

Data transfer rate: hard disk <memory <<cache <CPU

Two concepts mentioned above: main memory and working memory

  • Main memory: the computer's memory, which is often mentioned 8G memory, 16G memory

  • Working memory: but we instantiate new student, then age = 25 is also stored in main memory

    • When three threads access the age variable in the student at the same time, each thread will copy a copy to its own working memory, thus realizing the copy of the variable

    image-20200309154435933

That is: the visibility of the JMM memory model refers to that when the value in the main memory area is written and changed by a thread, other threads will immediately know the changed value and get the changed value again.

Cache coherency

Why can other threads know immediately after a certain value in the main thread is changed? In fact, bus sniffing technology is used here

Before talking about sniffing technology, let's first talk about the problem of cache coherence, that is, when multiple processor computing tasks involve the same main memory area, it may lead to inconsistent cache data.

In order to solve the problem of cache consistency, each processor needs to follow some protocols when accessing the cache, and operate according to the protocol when reading and writing. Such protocols mainly include MSI, MESI, and so on.

MONTHS

When the CPU writes data, if the operating variable is found to be a shared variable, that is, a copy of the variable exists in other CPUs, it will send a signal to other CPUs to set the cache line of the memory variable to be invalid, so when other CPUs read When this variable is found, it is found that the cache line that caches the variable is invalid, then it will be re-read from the memory.

Bus sniffing

So how do you find out whether the data is invalid?

Here is the use of bus sniffing technology, that is, each processor checks whether its cache value has expired by sniffing the data spread on the bus. When the processor finds that the memory address corresponding to its cache line has been modified, it will The cache line of the current processor is set to an invalid state. When the processor modifies this data, it will read the data from the memory to the processor cache again.

Bus storm

What are the disadvantages of bus sniffing technology?

Because Volatile's MESI cache coherency protocol requires constant sniffing from the main memory and CAS cycles, invalid interaction will cause the bus bandwidth to reach its peak. Therefore, do not use the volatile keyword in large quantities. As for when to use volatile, when to use locks, and Syschonized, it needs to be based on actual scenarios.

Features of JMM

Three characteristics of JMM, volatile only guarantees two, visibility and order, and does not satisfy atomicity

  • Visibility
  • Atomicity
  • Orderliness

Visibility code verification

But when we do not add any modification to the member variable, we cannot perceive the modified value of other threads

package com.moxi.interview.study.thread;

/**
 * Volatile Java虚拟机提供的轻量级同步机制
 *
 * 可见性(及时通知)
 * 不保证原子性
 * 禁止指令重排
 *
 */

import java.util.concurrent.TimeUnit;

/**
 * 假设是主物理内存
 */
class MyData {
    
    

    int number = 0;

    public void addTo60() {
    
    
        this.number = 60;
    }
}

/**
 * 验证volatile的可见性
 * 1. 假设int number = 0, number变量之前没有添加volatile关键字修饰
 */
public class VolatileDemo {
    
    

    public static void main(String args []) {
    
    

        // 资源类
        MyData myData = new MyData();

        // AAA线程 实现了Runnable接口的,lambda表达式
        new Thread(() -> {
    
    

            System.out.println(Thread.currentThread().getName() + "\t come in");

            // 线程睡眠3秒,假设在进行运算
            try {
    
    
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            // 修改number的值
            myData.addTo60();

            // 输出修改后的值
            System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);

        }, "AAA").start();

        while(myData.number == 0) {
    
    
            // main线程就一直在这里等待循环,直到number的值不等于零
        }

        // 按道理这个值是不可能打印出来的,因为主线程运行的时候,number的值为0,所以一直在循环
        // 如果能输出这句话,说明AAA线程在睡眠3秒后,更新的number的值,重新写入到主内存,并被main线程感知到了
        System.out.println(Thread.currentThread().getName() + "\t mission is over");

        /**
         * 最后输出结果:
         * AAA	 come in
         * AAA	 update number value:60
         * 最后线程没有停止,并行没有输出  mission is over 这句话,说明没有用volatile修饰的变量,是没有可见性
         */

    }
}

The output result is

image-20200309162154191

Finally, the thread did not stop, and the sentence mission is over was not output in parallel, indicating that there is no variable modified with volatile, and there is no visibility

When we modify the member variables in the MyData class, and add the volatile keyword modification

/**
 * 假设是主物理内存
 */
class MyData {
    
    
    /**
     * volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知
     */
    volatile int number = 0;

    public void addTo60() {
    
    
        this.number = 60;
    }
}

The final output is:

image-20200309162314054

The main thread has also been executed, indicating that the volatile modified variable has the JVM lightweight synchronization mechanism and can perceive the modified value of other threads.

Guess you like

Origin blog.csdn.net/weixin_43314519/article/details/110195157