volatile关键字

简介: volatile关键字

引入

学习这个之前,先来看这样一段代码:

import java.util.Scanner;
 
public class ThreadTest3 {
    public static int flag = 0;
    public static void main(String[] args) {
 
        Thread t1 = new Thread(() -> {
            while(flag == 0) {
                //循环体里,啥都不写
            }
            System.out.println("线程结束");
        });
 
        Thread t2 = new Thread(() -> {
            Scanner sc = new Scanner(System.in);
            flag = sc.nextInt();
        });
 
        t1.start();
        t2.start();
    }
}

运行一下,看一下结果:

按理来说:当修改flag的值之后,就应该不满足t1的条件了,t1就会结束.但显然结果不是这样的,让我们来分析一下原因:

这主要是因为两条核心指令:

1.load读取内存中的flag放到cpu的寄存器里.

2.拿着寄存器的值和0比较(条件跳转指令)

主要就是两个要点:

1.由于一开始flag为1,一开始load操作的执行结果,都是将0放入到寄存器中,但如果是输入的值的话,可能就是好几秒之后了,而这几秒钟的load操作,可能就很多了(上百亿次)

2.load的操作开销远远超过条件跳转指令(因为访问寄存器操作速度远远高于内存,就会把load优化掉)

因为编译器发现,每次循环都要读取内存,开销太大,于是将读取内存的操作优化成读取寄存器的操作.

由上,可以发现输入的值是存入到了内存里面,而while哪里判断使用的flag一直都是从寄存器中读取的.因此输入的值并没有任何影响,那么有什么方法能够解决这种问题呢?这就用到了volatile关键字.

volatile关键字

核心:volatile能够保证内存可见性

volatile修饰的变量,能够保证"内存可见性".(强制读取内存),这就解决上面的问题了

禁止指令重排序,针对这个被volatile修饰的变量的的读写操作相关指令,是不可以重排序的.

让我们看一下修改的代码及结果

public class ThreadTest3 {
    public static volatile int flag = 0;
    public static void main(String[] args) {
 
        Thread t1 = new Thread(() -> {
            while(flag == 0) {
                //循环体里,啥都不写
                //啥都不写可能一秒内能运行上百亿次1
                //这时被优化的迫切程度就很高
                //譬如Thread.sleep(1000)的操作可以将运行次数优化到1000次
                //这时优化的操作就不必要了
            }
            System.out.println("线程结束");
        });
 
        Thread t2 = new Thread(() -> {
            //相当于t2改变了线程,但是t1没有看见内存的变化
            Scanner sc = new Scanner(System.in);
            flag = sc.nextInt();
        });
 
        t1.start();
        t2.start();
    }
}

显然解决了问题.

代码在写入volatile关键字的时候,

1.改变线程工作内存中volatile副本的值

2.将改变后的副本的值从工作内存刷新到主内存

代码在读取volatile修饰变量的时候,

1.从主内存中读取volatile关键字修饰的最新值到线程的工作内存中

2.从工作内存中读取volatile关键字的副本

前面我们讨论内存可见性时就说了`,直接访问工作内存(实际是CPU的寄存器或者CPU的缓存),速度非常快,但是可能出现数据不一致的情况.

加上volatile,强制读写内存,速度变慢了,但是数据更准确了.

相关文章
|
4月前
|
机器学习/深度学习 人工智能 编解码
AI视频去字幕技术完全指南:原理、方法与工具对比(2026版)
本文深度解析AI视频去字幕技术,涵盖原理(OCR检测+GAN修复+时序一致性)、主流工具横评、分步实操教程及短视频、教育、影视等六大行业应用。适合创作者、自媒体人与技术爱好者,20分钟掌握高效去字幕方法。
1709 0
|
存储 机器学习/深度学习 人工智能
【数据结构】——期末复习题题库(1)
【数据结构】——期末复习题题库(1)
【数据结构】——期末复习题题库(1)
|
数据采集 数据处理 Python
探索数据科学前沿:Pandas与NumPy库的高级特性与应用实例
探索数据科学前沿:Pandas与NumPy库的高级特性与应用实例
291 0
课时143:综合实战:宠物商店
宠物商店系统通过定义宠物接口(`Pet`),实现了宠物的上架、下架和查询功能。具体流程如下: 1. **定义宠物标准**:通过接口 `Pet` 定义宠物的基本属性,如名字和颜色。 2. **宠物商店类**:`Petshop` 类使用链表保存多个宠物信息,提供添加、删除和关键字查询功能。 3. **实现具体宠物类**:如 `Cat` 和 `Dog` 类实现 `Pet` 接口,定义具体的宠物属性和行为。 4. **操作演示**:通过 `JavaDemo` 类演示如何开店、添加宠物并进行查询。 总结:该系统以接口为标准,确保所有符合标准的对象可以灵活管理,体现了面向接口编程的优势。
331 0
|
机器学习/深度学习 算法 数据处理
深度学习500问——Chapter03:深度学习基础(3)
深度学习500问——Chapter03:深度学习基础(3)
834 1
|
Shell Linux
在Linux中,使用bash shell实现条件判断和循环结构的例子是什么样的?
在Linux中,使用bash shell实现条件判断和循环结构的例子是什么样的?
|
安全 Linux Shell
Linux服务器配置SSH免密码登录后,登录仍提示输入密码(一次真实的问题排查解决记录)
Linux服务器配置SSH免密码登录后,登录仍提示输入密码(一次真实的问题排查解决记录)
1304 0
|
API 数据安全/隐私保护
gitlab--内置的环境变量、自定义环境变量
gitlab--内置的环境变量、自定义环境变量
1062 0
|
前端开发 JavaScript 编译器
vue3中的单文件组件<script setup>和setup函数区别 详解(二)
vue3中的单文件组件<script setup>和setup函数区别 详解
620 0