java EE初阶 — volatile关键字保证内存可见性

news2024/12/24 20:55:03

文章目录

    • 1.volatile保证内存可见性
      • 1.1 如何保证内存可见性
      • 1.2 java 内存模型(JMM)
    • 2.volatile 不保证原子性

1.volatile保证内存可见性

先来看一段代码

package thread;

import java.util.Scanner;

class MyCounter {
    public int flag = 0;
}

public class ThreadDemo17 {
    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 input = new Scanner(System.in);
            System.out.println("请输入一个人非0整数:");
            myCounter.flag = input.nextInt();//输入一个 整数
        });
        t1.start();
        t2.start();
    }
}

t1 线程要快速循环读取。t2 线程要修改 flag 以保证可以跳出循环。

这段代码的预期是在输入一个非0整数后,线程1跳出循环输出 t1循环结束


Thread-0 就是 t1 ,因为是要循环,所以状态是 RUNNABLE


Thread-2 就是 t2 ,状态是 RUNNABLE但是其实它是在阻塞等待的。


输入非0整数发现程序并没有结束。


同时可以看到 t2 线程已经不在了(执行完了),但是 t1还在,程序会继续执行循环。

这就是 内存可见性 问题。

使用汇编来理解可以分两步:

  1. load 把内存中的 flag 的值读取到寄存器里。
  2. cmp 把寄存器的值和 0 进行比较,根据比较的结果,决定下一步往哪个地方执行(条件跳转指令)

上述的循环会执行很多次,在 t2 真正修改之前。load 得到的结果都是一样的。
另一方面。load 操作和 cmp 操作相比,速度慢非常非常多!!!

相对于 cmp 来说, load 的执行速度太慢了,再加上1反复得到的结果都一样,
JVM 就做出了一个非常大胆的决定。不再真正的重复 load 了,判定好像不会修改 flag 的值了。
于是干脆就只读取一次就好了。

这其实是编译器优化的一种方式。

内存可见性问题:

一个线程针对一个变量进行读取操作,同时另一个变量针对这个变量进行修改,
此时读到的值,不一定是修改之后的值。

这个读线程没有感知到变量的变化。

内存可进行问题本质上是编译器/jvm 在多线程的环境下优化时产生误判了。

1.1 如何保证内存可见性

volatile 关键字能保证内存可见性

此时需要手动给 flag 变量加上 volatile关键字。
意思就是告诉编译器,这个变量是 “易变”的,一定要每次重新读取这个变量的的内存内容。
因为不一定什么时候就会反生改变,不能随便的优化了。

class MyCounter {
   volatile public int flag = 0;
}


加上 volatile 后,得到了预期的结果。

上面的编译器优化的情况,也不是始终会出现的。(编译器也可能会出现误判)

下面来调整以下代码,使用 sleep 来控制循环速度。

package thread;

import java.util.Scanner;

class MyCounter {
   public int flag = 0;
}

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

        Thread t1 = new Thread(() -> {
            while (myCounter.flag == 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1循环结束");
        });

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



观察结果即可发现,即使没有 volatile 的参与,程序依然达到了预期结果。

但是为了保证稳妥还是建议加上 volatile。

1.2 java 内存模型(JMM)

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


把上诉内容里的 “主内存” ,改为 “内存”。
把 “工作内存” 改为 “CPU寄存器”。或许可以使上面的内容更好的理解。


这里的工作内存不一定只是 CPU 的寄存器,还可能包括 CPU 的缓存 cache

CPU 读取寄存器,速度比读取内存快多了。
因此就会在 CPU 内部引入缓存 cache

寄存器存储空间小,读写速度块,但是它的价格比较贵。
中间有一个cache存储空间,读写速、成本居中。
内存存储空间大,读写速度满,相对于寄存器来说比较便宜。

当 CPU 读取一个内存数据的时候,可能是直接读取内存、也可能是读取 cache 、还可能是读取寄存器。

引入 cache 之后,硬件结构就更复杂了。

工作内存(工作存储区):CPU 寄存器 + CPU 的 cache

一方面是为了表述简单,另一方面是为了避免涉及到硬件的细节和差异。

2.volatile 不保证原子性

package thread;

class Counter {
    volatile public int count = 0;

     public void add() {
         count++;
    }
}

public class ThreadDemo15 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count:" + counter.count);
    }
}

volatile 和 synchronized 有着本质的区别。
synchronized 能够保证原子性,volatile 保证的是内存可见性。


此时可以看到, 最终 count 的值仍然无法保证是 100000

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt实现编辑框失去焦点隐藏功能

今天来为大家分享一个小功能&#xff0c;首先看实现的效果吧~ 功能讲解&#xff1a; QLineEdit控件进行文本编辑&#xff0c;点击保存按钮后&#xff0c;隐藏编辑框和保存按钮&#xff0c;仅展示编辑内容&#xff0c;当鼠标点击空白处时&#xff0c;同样隐藏编辑框、隐藏保存按…

Dubbo前后端分离监控中心搭建

监控中心&#xff0c;因为监控中心现在前后端分离&#xff0c;所以相比与老版有变动&#xff0c;首先下载压缩包 官网: https://github.com/apache/dubbo-admin/tree/develop 下载安装包&#xff0c;使用IDEA打开解压好的dubbo-admin-develop文件夹&#xff0c;稍等片刻让Mav…

CTFHUB-web-RCE

eval执行 <?php if (isset($_REQUEST[cmd])) {eval($_REQUEST["cmd"]); } else {highlight_file(__FILE__); } ?> 看下当前目录 ?cmdprint_r(getcwd());可以执行命令 print_r(system(ls));查看上级目录?cmdprint_r(system(ls ../../../)); 直接cat flag?c…

使用模拟退火(SA)和Matlab的车辆路径问题(VRP)(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

signature verification failed csb502报错

因为这个报错特意搜了下csb是个什么东西。 简单说csb就是云服务总线&#xff0c;提供API对外开放能力&#xff0c;提供认证鉴权:访问签名&#xff0c;检查请求是否合法&#xff0c;是否已授权的。 从前端角度来看&#xff0c;就是 1.客户端通过接口发起请求&#xff0c;携带必要…

SpringBoot+Vue项目餐饮管理系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

放弃60万年薪考公!程序员完败公务员?

都说宇宙的尽头是考公&#xff0c;受到疫情的影响&#xff0c;我国的考公队伍也是越来越庞大。 考公队伍中不乏程序员。在某平台上&#xff0c;就有人分享了自己程序员朋友的考公经历&#xff1a; 发帖人的朋友原来是在阿里工作的&#xff0c;在他最高薪的时候能拿到60万的年薪…

栈的介绍以及使用数组模拟栈的入栈和出栈

❤️一名热爱Java的大一学生&#xff0c;希望与各位大佬共同学习进步❤️ &#x1f9d1;个人主页&#xff1a;周小末天天开心 各位大佬的点赞&#x1f44d; 收藏⭐ 关注✅&#xff0c;是本人学习的最大动力 感谢&#xff01; &#x1f4d5;该篇文章收录专栏—数据结构 目录 栈…

[U3D ShaderGraph] 全面学习ShaderGraph节点 | 第三课 | Input/Gradient

📣📣📣本专栏所有内容在完结之前全部为试读模式,专栏完结之后会取消试读模式,如果感觉内容还不错的话请支持一下📣📣📣 ShaderGraph是可视化的着色器编辑工具。您可以使用此工具以可视方式创建着色器。 本专栏可以让你更了解ShaderGraph中每个节点的功能,更自如的…

eBPF汇编指令你还不知道?看这一篇文就够了

【好文推荐】 一文看懂linux 内核网络中 RPS/RFS 原理 怎么在Windows下使用Makefile文件 浅析linux内核网络协议栈--linux bridge 大家好&#xff0c;我是你们的彦祖&#xff0c;今天这篇文主要介绍 eBPF 的指令系统&#xff0c;对于想深入理解 eBPF 的同学千万不要错过&#x…

实验五 计数/定时器的设计【Verilog】

实验五 计数/定时器的设计【Verilog】前言推荐实验五 计数/定时器的设计一、实验目的二、实验环境三、实验任务四、实验原理与实验步骤五、实验思考最后前言 以下内容源自Verilog实验 仅供学习交流使用 推荐 Verilog 实验五 计数/定时器的设计 一、实验目的 掌握二进制计…

计时器Timing Wheel 时间轮算法

文章目录1. 前言2. 什么是时间轮算法&#xff1f;2.1 单层时间轮2.2 多层时间轮2.2.1 增加轮次的概念2.2.2 多层次时间轮2.3 小结3. 实现案例3.1 Kafka中的时间轮3.1.1 任务的添加3.1.2 时间轮的推进SystemTimer3.1.3 小结1. 前言 计时器对于故障恢复、基于速率的流量控制、调…

[yolov5] yolo的数据标签格式

yolov5 的标签格式 参考链接&#xff1a; https://github.com/ultralytics/yolov5/issues/9816 翻译内容 你好!。感谢您询问YOLOv5&#x1f680;数据集格式。用于分割的XY坐标与用于长方体中心的标准坐标相同。 为了正确训练&#xff0c;您的数据必须为YOLOv5格式。有关数…

数据结构与算法的基本概念

前言 技术学得再多&#xff0c;再好还不如将基础学扎实。欠下的迟早是要还的。与其在以后后悔当初不好好学&#xff0c;还不如在大学期间将该学的知识学透。《数据结构与算法》确实不好学&#xff0c;但大学四年下来&#xff0c;还怕啃不下一本书&#xff1f;&#xff1f;正是基…

自动驾驶技术

高精地图&#xff08;HD Maps&#xff09;&#xff1a;支持其他模块 定位&#xff08;Localization&#xff09;&#xff1a;讨论汽车如何确定他所处的位置&#xff0c;汽车利用激光和雷达数据&#xff0c;将这些传感器感知内容与高分辨地图进行对比&#xff0c;这种对比使得汽…

Scanpy plot umap的color编码, Scanpy 的color map 如何设置?

Scanpy plot umap的color编码&#xff0c; Scanpy 的color map 如何设置&#xff1f;关键词palette&#xff08;调色板&#xff09; https://scanpy.readthedocs.io/en/stable/generated/scanpy.pl.umap.html?highlightpl.umap#scanpy.pl.umap https://scanpy.discourse.grou…

JavaScript -- 08. 数组介绍

文章目录数组1 数组简介1.1 创数组1.2 向数组添加元素1.3 读取数组中的元素1.4 获取数组长度2 数组的遍历2.1 使用for循环遍历2.2 for-of遍历3 数组的方法3.1 Array.isArray()3.2 at()3.3 concat()3.4 indexOf()3.5 lastIndexOf()3.6 join()3.7 slice()3.8 push()3.9 pop()3.10…

Android Jetpack-Compose相关

Android Jetpack-Compose相关 一、什么是Compose&#xff1f; Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API&#xff0c;可以帮助您简化并加快 Android 界面开发&#xff0c;打造生动而精彩的应用。它可让您更快…

Go学习之路-环境搭建

默认运行设备系统&#xff1a;MacOS 安装 安装包下载地址&#xff08;下面3个都可以&#xff09;&#xff1a; https://studygolang.com/dl https://golang.google.cn/dl/ https://golang.org/dl/ 我这里选择 pkg包、一键安装、默认的安装路径 /usr/local/go 环境 设置go语言…

[附源码]计算机毕业设计springboot在线图书销售系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…