volatile 关键字

我们先看这段代码

class MyCounter {
    public int flag = 0;
}

public class ThreadDemo15 {
    public static void main(String[] args) {
        MyCounter myCounter = new MyCounter();

        Thread t1 = new Thread(() -> {
            while (myCounter.flag == 0) {
                // 这个循环体咱们就空着
            }
            System.out.println("t1 循环结束");
        });

        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数: ");
            myCounter.flag = scanner.nextInt();
        });

        t1.start();
        t2.start();
    }
}

        上述代码的逻辑是,t1 有个循环,flag 作为判断循环条件的变量,t2 修改这个变量,修改完了 t1 线程按道理也就结束了。

        但运行出来的结果却并没有结束,这个情况叫做“内存可见性问题”,即一个线程针对一个变量进行读取操作,另一个线程针对这个变量进行修改,此时读到的值,不一定是修改后的,因为这个变量没有感知到变量的变化。(编译器 / JVM在多线程环境下优化时产生了误判)

        这里使用汇编来理解大概就是以下两步操作:

1. load,把内存中的 flag 值读到寄存器中

2. cmp,把寄存器中的值和 0 进行比较,然后根据结果决定下一步(条件跳转指令)

        由于 load 执行速度太慢(相对于 cmp ),再加上反复 load 的结果都是一样的,JVM 就做了一个大胆的决定,只进行一次 load。不再真正的重复 load 了,因为判定没人修改 flag 值(编译器优化的一种方式)。

        此时,就需要程序猿手动进行过干预了 → 给 flag 这个变量加上 volatile 关键字,意思是告诉编译器这个变量是“易变的”,这个时候编译器就不会进行这种激进式的优化了。

static class Counter {
    public volatile int flag = 0;
}

// 执行效果
// 当用户输入非0值时, t1 线程循环能够立即结束.

从 JMM(Java Memory Model、Java内存模型)的角度表述内存可见性问题:

在 Java 程序里,有主内存,每个线程也有自己的工作内存 ( t1 和 t2 的工作内存不是同一个东西 ) t1 线程在进行读取的时候,只是读取了工作内存中的值.                                                                  t2 线程在进行修改的时候,先修改工作内存的值,然后再把工作内存的内容同步到主内存中          但是由于编译器优化,导致 t1 没有重新从主内存中同步数据到工作内存,因此读到的结果就是“修改之前”的。

把“主内存”代替为“内存”,“工作内存”代替为“工作存储区”这样就好理解了。工作内存(工作储存区)并非是所说的内存,而是 CPU 寄存器 + CPU 的 cache(缓存)

volatile 能保证内存可见性

volatile 修饰的变量, 能够保证 "内存可见性"

代码在写入 volatile 修饰的变量的时候

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

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

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

从主内存中读取volatile变量的最新值到线程的工作内存中

从工作内存中读取volatile变量的副本

前面我们讨论内存可见性时说了, 直接访问工作内存(实际是 CPU 的寄存器或者 CPU 的缓存), 速度

非常快, 但是可能出现数据不一致的情况.

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

volatile 不保证原子性

        volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性。因此如果某个代码既要考虑原子性也要考虑内存可见性,就把 synchronized 和 volatile 都用上即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/537160.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

平板设备IP地址设置指南

在数字化时代,平板电脑作为便携且功能强大的设备,广泛应用于日常生活和工作中。为了确保平板能够正常接入网络并与其他设备进行通信,正确设置IP地址是至关重要的。虎观小二将为您介绍如何设置平板的IP地址,帮助您轻松完成网络配置…

JavaScript ECMAScript标准的与时俱进:从ES6至ES14的革新之路与关键技术特性剖析

ECMAScript(通常缩写为ES)是一种标准化的脚本语言规范,由ECMA International(前身为European Computer Manufacturers Association,欧洲计算机制造商协会)制定。自1997年发布首个版本以来,ECMAS…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记13:RTC实时时钟

系列文章目录 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装 嵌入式|蓝桥杯STM32G431(…

PAN1026蓝牙收发芯片

1 概述 PAN1026 系列产品是一款低成本、高集成度的无线 BLE 数据收发芯片,工作在射频 2400MHz ~2483MHz 的通用 ISM 频段。具有较低的系统应用成本,只需要一个 MCU 和少量外部无 源组件即可构建满足无线应用的系统。同时,操作方式非…

七大免费SSL证书获取方法

为了保护用户数据的安全,越来越多的网站开始使用SSL证书。SSL证书是一种数字证书,它能够为网站提供一个安全的加密连接,从而保护用户的敏感信息。然而,购买SSL证书需要一定的费用,这对于一些小型企业或个人来说可能是一…

C++ | Leetcode C++题解之第23题合并K个升序链表

题目: 题解: class Solution {// 21. 合并两个有序链表ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {auto dummy new ListNode(); // 用哨兵节点简化代码逻辑auto cur dummy; // cur 指向新链表的末尾while (list1 && list2) {if (list1…

test4121

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和…

强化学习基础概念入门

文章目录 1. 什么是强化学习?2. 强化学习的基本元素3. 相关衍生元素3.1 策略(Policy)3.2 状态转移(State Transition)3.3 回报(Return)3.4 价值函数(Value Function) 4. 算法分类4.1 按环境是否已知划分4.2 按学习方式划分4.3 按学习目标划分 参考资料 1. 什么是强化…

本地开发nginx代理服务器(2024-04-10)

1、nginx 解释 nginx 是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器。 在性能上,Nginx占用很少的系统资源,能支持更多的并发连接,达到更高的访问效率; 在功能上,Nginx是优…

QT:QMainWindow、ui界面、资源文件的添加、信号和槽

1.练习:使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(…

OJ 古籍翻译 【进制转化】【C】

这里有两个考察点,一个是进制转换,还有一个是以字符串输出 知识点: sprintf(参数一,参数二,参数三) 将参数三按参数二的格式转换后存入参数一中 sprintf与printf用法基本一样,只是sprintf是将值打印到指…

SketchBook2014 下载地址及安装教程

SketchBook是一款专业级别的绘图与绘画软件。它提供了丰富的绘画工具和创意功能,适用于艺术家、插画师、设计师和数字艺术爱好者。SketchBook具有直观的用户界面,简单而强大的绘制工具,能够帮助用户在数字平台上创造出精美的艺术作品。 Sket…

哪些工作不会被AI替代:人类能力地形图

这一轮AI,到底对人有多强的替代性?这一轮AI的可靠性,是之前任何一代所谓的人工智能都不能比的。 在国内,AI开始被用来筛选简历,而在国外,亚马逊正在用算法跟踪仓库工人的生产率,顺道还会给生产率…

橘子学JDK之JMH-02(BenchmarkModes)

一、案例二代码 这次我们来搞一下官网文档的第二个案例,我删除了一些没用的注释,然后对代码做了一下注释的翻译,可以看一下意思。 package com.levi;import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import …

RocketMQ之Topic和Tag最佳实践

一、Topic是什么?它的作用? Topic即主题,是消息队列中用于对消息进行分类和组织的一种机制,它有以下三种作用: 标识消息分类:RocketMQ的主题用于对消息进行分类和组织。通过为不同类型的消息分配不同的主题…

LeetCode-Java:6.Z字形变换

文章目录 题目解① 找规律 题目 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下: P A H N A P L S I I G Y I R之后&a…

2024Mathorcup(妈妈杯)数学建模C题python代码+数据教学

2024Mathorcup数学建模挑战赛(妈妈杯)C题保姆级分析完整思路代码数据教学 C题题目:物流网络分拣中心货量预测及人员排班 因为一些不可抗力,下面仅展示部分代码(很少部分部分)和部分分析过程,其…

[python] Numpy库用法(持续更新)

先导入一下 import numpy as np 一、np.random用法 生成随机整数:np.random.randint(low, high, size) low: 最小值high: 最大值size: 生成的数组大小(可以是多维,下面同理) 生成随机浮点数:np.random.uniform(low, …

下线圈和包扎

电机槽内放好 所有槽纸 槽内再放入引槽纸 作用是放线圈的时候避免划伤线 开始放线圈 绑了白色扎带的朝外面 线圈的一边放进去后,另一边还悬在外面 ,这里俗称 吊边。 为了保护吊边 ,在吊边处放一张大的绝缘纸 (如下图&#xff0…

32.WEB渗透测试-数据传输与加解密(6)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:31.WEB渗透测试-数据传输与加解密(5) 关于discuz3.5的源码内容和…