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,强制读写内存,速度变慢了,但是数据更准确了.

相关文章
|
负载均衡 测试技术 应用服务中间件
性能测试常见瓶颈分析及调优方法总结
性能测试常见瓶颈分析及调优方法总结
625 0
|
12月前
|
存储 分布式计算 数据可视化
大数据常用技术与工具
【10月更文挑战第16天】
650 4
|
jenkins 持续交付 开发者
自动化部署:使用Jenkins和Docker实现持续集成与交付
【8月更文挑战第31天】本文旨在为读者揭示如何通过Jenkins和Docker实现自动化部署,从而加速软件开发流程。我们将从基础概念讲起,逐步深入到实际操作,确保即使是初学者也能跟上步伐。文章将提供详细的步骤说明和代码示例,帮助读者理解并应用这些工具来优化他们的工作流程。
|
7月前
|
存储 监控 安全
课时17:阿里云智能制造解决方案:从中国制造到中国智造
阿里云智能制造解决方案融合云计算、大数据和人工智能,提供一站式设备接入管理,助力工业客户实现从“中国制造”到“中国智造”的跨越。通过解决数据孤岛、实时数据分析和智能算法,方案提升生产效率与良品率,降低运营成本,并确保信息安全。此外,支持远程监控与定制化服务,全面推动产业升级与创新。
222 0
|
11月前
|
供应链 安全 物联网安全
NIST(美国国家标准与技术研究院)在网络安全领域进行了多项创新
NIST(美国国家标准与技术研究院)在网络安全领域进行了多项创新
281 10
|
人工智能 Apache 流计算
Flink Forward Asia 2024 上海站|探索实时计算新边界
Flink Forward Asia 2024 即将盛大开幕!11 月 29 至 30 日在上海举行,大会聚焦 Apache Flink 技术演进与未来规划,涵盖流式湖仓、流批一体、Data+AI 融合等前沿话题,提供近百场专业演讲。立即报名,共襄盛举!官网:https://asia.flink-forward.org/shanghai-2024/
1245 33
Flink Forward Asia 2024 上海站|探索实时计算新边界
|
12月前
|
机器学习/深度学习 数据采集 人工智能
数据驱动的AI技术:如何通过深度学习提升图像识别精度
【10月更文挑战第18天】 数据驱动的AI技术:如何通过深度学习提升图像识别精度
372 0
|
安全 数据处理 开发者
Boost序列化与Protobuf比较:深入分析 (Boost Serialization vs. Protobuf: An In-depth Comparison)...
Boost序列化与Protobuf比较:深入分析 (Boost Serialization vs. Protobuf: An In-depth Comparison)...
467 1
|
存储 网络协议 安全
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(1)-初识Wireshark
【2月更文挑战第1天】《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(1)-初识Wireshark
631 3
如何在WordPress中添加新的页面?
如何在WordPress中添加新的页面?北京六翼建站总结了在 WordPress 中添加新页面的步骤如下: 登录 WordPress 后台。在仪表板上,点击左侧菜单中的“页面”选项。