Volatile总结

本文介绍了Java内存模型,强调了Volatile关键字的作用。Volatile确保了多线程环境下的线程可见性,防止指令重排序,但不保证原子性。通过MESI协议,Volatile在写操作时会加锁并通知其他CPU缓存失效,保证数据一致性。

Volatile总结

1.Java内存模型

java内存模型中 所有变量都存储在主内存(虚拟机内存)《主要指共享变量》,每个线程有着自己独有的工作内存,工作内存中保存了该线程使用的变量,这些变量来自主内存中变量的副本拷贝。线程对变量的所有操作都必须在工作内存中进行,不能直接读写主存中的变量,不同线程之间的工作内存时相互独立的,不可以访问其他线程的工作内存。

在线程工作时,把需要的变量从主内存中拷贝到自己的工作内存

在这里插入图片描述

当一个线程要使用一个共享变量时,并不会直接使用,而是使用它的一个副本,然后交互副本值 ,

当出现JMM的缓存不一致时可以总线加锁 也可以使用 MESI缓存一致性协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAVphV96-1616324208923)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210311155347951.png)]

java 中volatitle 就相当于使用了MESI缓存一致性

下面代码展示 volatile 的作用 -boolean类型的开关

/**
 * @Author: hqx
 * @Description:
 * @Date: Created in 12:01 2021/3/21
 */

/**
 * @author Administrator
 * @create 2021-03-21 12:01
 * @desc
 **/
public class volatilettest {
    //使用volatile 作为 boolean类型 开关
    private static boolean flag1 = false;
    private static volatile boolean flag2 = false;

    public static void main(String[] args) throws InterruptedException {
        volatilettest vo = new volatilettest();
        new Thread(() -> {
            //flag为false 时一直循环
            System.out.println("111工作中---");
            while (!flag1) {
            }
            System.out.println("111工作结束--");
        }).start();


        new Thread(() -> {
            //flag为false 时一直循环
            System.out.println("222工作中---");
            while (!flag2) {
            }
            System.out.println("222工作结束--");
        }).start();
        //主线程
        Thread.sleep(1000);

        vo.change1();
        vo.change2();
    }

    public void change1() {
        System.out.println("1进入改变函数");
        flag1 = true;
        System.out.println("1改变完成-");
    }

    public void change2() {
        System.out.println("2进入改变函数");
        flag2 = true;
        System.out.println("2改变完成-");
    }
}

在这里插入图片描述

2.volatitle 原理实现

会在汇编语言中加入一个lock指令,该指令会将当前处理器缓存行的数据立刻写回到系统的内存中,并且这个写回内存的操作还会引起在其他cpu里缓存了该内存地址的数据无效**(MESI协议)cpu总线嗅探机制**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMkuSjeK-1616324208941)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210311163416047.png)]

工作内存会与主内存进行数据交互读写,主要由8种操作进行:

lock(锁定):作用于主内存变量,把一个变量标识为一条线程独占

unlock(解锁):

read(读取):作用于主内存变量,将一个变量值从主内存传输到线程的工作内存

load(载入):作用于工作内存变量,他把通过read操作从主内存中得到的变量值放入工作内存的变量副本中

use(使用):作用域工作内存变量,将工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个要给变量赋值的字节码执行时会使用这个操作

assign(赋值):作用于工作内存的变量,它把一个从执行引擎处接收到的值赋给工作内存中的一个变量,每当虚拟机遇到一个给变量赋值的字节码时就会执行这个操作

stroe(存储):作用域工作内存变量,将工作内存中的一个变量的值传送到主内存中

write(写入):作用域主内存变量 ,他把通过store操作从工作内存中读取的变量值放入主内存的变量中

上述8个操作存在约束:

1.read+load 与 store+write 可以成对出现

2.不允许线程丢弃它最近的assign操作 ,即变量在工作内存中改变之后,必须同步写回主内存

3.不允许线程把没有经过assign操作的变量写回到主存

4.一个新的变量只能在主内存中诞生,不允许在工作内存中国使用未经初始化的变量,即在进行use 之前必须load 在store之前必须assign

5.对一个变量执行lock操作

下图为上面boolean开关的实例的一个过程演示
在这里插入图片描述

通过read 读取主内存中的变量并传送到工作内存,load将read获取的值赋值给工作内存中对应的变量,use读取initFlag的值给到执行引擎,assign将更新后的值写回到工作内存中的initFlag 在通过store,write写回到主内存

volatitle 会在store和write这个过程中加锁,并且在触发cpu嗅探机制

volatitle作用

在通过store,write写回到主内存

volatitle 会在store和write这个过程中加锁,并且在触发cpu嗅探机制

[外链图片转存中…(img-RzVV6eiC-1616324208947)]

volatitle作用

线程可见性,可以禁止指令重排 ,保证顺序性。不具备线程安全性,原子性

<think> 我们正在总结关于volatile关键字的关键知识点,基于用户提供的选择题选项及引用内容。选项如下: A. 优化器在用到volatile变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份 B. 一个参数不可以既是const又是volatile C. volatile适用于多线程应用中被几个任务共享的变量 D. 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后再取变量值时,就直接从寄存器中取值 根据之前的分析: - A正确:volatile强制每次从内存读取,避免使用寄存器缓存[^1][^2][^3]。 - B错误:参数可以同时是const和volatile,比如只读的状态寄存器[^1]。 - C正确:volatile用于多线程共享变量,确保每次读取最新值,但注意它不保证原子性[^1]。 - D正确:描述了非volatile变量可能被编译器优化(缓存到寄存器),而volatile会阻止这种优化[^2]。 因此,我们需要总结的关键知识点包括: 1. volatile的作用:告诉编译器不要优化对该变量的访问,每次都必须从内存中重新读取。 2. volatile的使用场景:硬件寄存器、中断服务程序、多线程共享变量等。 3. volatile与const可以同时使用,表示变量程序不可修改但可能被外部改变。 4. 编译器优化行为:非volatile变量可能被缓存,volatile变量则禁止此优化。 根据题目,选项B的说法错误,所以我们在总结时要明确指出这一点。 接下来,我们按照以下结构总结: 1. volatile的核心作用 2. volatile的使用场景(包括多线程) 3. volatile与const的关系 4. 编译器优化的对比(volatile与非volatile) 同时,我们将生成相关问题。 注意:引用内容需要标注在相应位置。 </think> ### volatile关键字核心知识点总结 #### 1. volatile的核心作用:禁用寄存器缓存优化 volatile强制编译器每次访问变量时直接从内存地址读取/写入,禁止使用寄存器中的缓存值[^2][^3]。 **示例场景**: ```c volatile int sensor; while(sensor == 0); // 每次循环都从内存读取sensor值 ``` 若不声明volatile,编译器可能优化为: ```assembly LOAD R1, [sensor] ; 首次读取存入寄存器 LOOP: CMP R1, #0 ; 后续只比较寄存器值 JZ LOOP ``` #### 2. volatile与const可共存 一个变量可同时声明为`const volatile`,表示: - **const**:程序不可修改该变量 - **volatile**:外部因素(如硬件)可能改变其值 典型应用如只读硬件状态寄存器[^1]: ```c const volatile uint32_t* STATUS_REG = (uint32_t*)0xFFFF0000; ``` #### 3. 多线程场景的适用性与局限性 - **适用性**:volatile适用于多线程共享变量,确保线程读取最新值(如标志位)[^1] - **局限性**: - ❌ 不保证操作的**原子性**(如`i++`非原子操作) - ❌ 不提供**内存屏障**(可能发生指令重排序) ⚠️ 需配合互斥锁或原子操作实现线程安全[^1][^3] #### 4. 编译器优化行为对比 | **变量类型** | **读取行为** | **风险场景** | |------------|------------|------------| | **普通变量** | 可能缓存到寄存器后续复用 | 中断/多线程中值不同步[^2] | | **volatile变量** | 每次强制从内存读取 | 无缓存一致性问题 | #### 5. 典型应用场景 - **硬件寄存器访问**:内存映射的硬件寄存器[^3] - **中断服务程序**:主程序与ISR共享的变量[^4] - **多线程标志位**:线程间通信的简单状态标志[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值