【JUC】十九、volatile与内存屏障

文章目录

  • 1、volatile的两大特性
  • 2、volatile的四大内存屏障
  • 3、分类
  • 4、happens-before之volatile变量重排规则
  • 5、读写屏障插入策略

1、volatile的两大特性

被volatile修饰的变量有两大特点:

  • 可见性
  • 有序性

关于volatile的可见性,也即volatile的内存语义:

当写一个volatile变量时,JMM会把该线程对应的本地工作内存中的共享变量值立即刷新回主内存中,前面的修改对后面所有线程是可见的。

这一条算是对应了前面说到的线程工作内存里的修改没来得及刷到主内存就被挂起引起的问题。

当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到主内存中读取最新共享变量

所以volatile的写内存语义是直接立马刷新到主内存中,读的内存语义是直接从主内存中读取。再说有序性,即禁止进行重排。

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序,重排后的指令不能改变原有的串行语义,因此不存在数据的依赖关系,则可以重排序,反之,禁止重排序。

而volatile可以保证可见性和有序性的依靠 ⇒ 内存屏障Memory Barrier

2、volatile的四大内存屏障

类比到生活,没有管控,顺序难保,一片混乱:

在这里插入图片描述

因此考虑设定规则,禁止乱序,比如武警人墙:

在这里插入图片描述

再比如强制排队的围栏,防止混乱和插队导致的冲突:

在这里插入图片描述

内存屏障(也称内存栅栏,屏障指令),是一类同步屏障指令,这些指令就是上面的一个个组成人墙的警察,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作,避免代码重排序。

内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了Java内存模型中的可见性和有序性(禁重排),但volatile无法保证原子性。

内存屏障实现可见性是因为它的出现代表着:

  • 内存屏障之前的所有与操作都要回写到主内存

  • 内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果

更细致的说是:

  • 写屏障 (Store Memory Barrier):告诉处理器在写屏障之前将所有存储在高速缓存(store bufferes)中的数据同步到主内存。也就是说当看到Store屏障指令,就必须把该指令之前所有写入指令执行完毕才能继续往下执行。

  • 读屏障(Load Memory Barrier):会让工作内存或CPU高速缓存当中的缓存数据失效,也就是说在Load屏障指令之后就能够保证后面的读取数据指令一定能够读取到最新的数据。

在这里插入图片描述

因此重排序时,不允许把内存屏障之后的指令重排序到内存屏障之前。一句话:对一个volatile变量的写,先行发生于任意后续对这volatile变量的读,也叫写后读。

happens-before先行发生原则的保证就是内存屏障,happens-before就像接口,定义了方法叫啥,而内存屏障则是具体的落地

3、分类

内存屏障,粗看有两种:

读屏障(Load Barrier)

在读指令(类比代码中的get)之前插入读屏障,会让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据。

写屏障(Store Barrier)

在写指令(类比代码中的set)之后插入写屏障,会强制把写缓冲区(线程的工作内存)的数据刷回到主内存中。

细分(Unsafe.class -> Unsafe.java -> Unsafe.cpp -> OrderAccess.hpp)有四种:

在这里插入图片描述

  • 读读屏障
  • 写写屏障
  • 读写屏障
  • 写读屏障

含义如下:
在这里插入图片描述
以第一个为例:

Load1;LoadLoad;Load2

示意图:

在这里插入图片描述

即在两次读load1和load2之间加了一个读读屏障,即必须先load1执行,再load2执行,不允许重排。

4、happens-before之volatile变量重排规则

重排序有可能影响程序的执行和实现,因此,我们有时候希望告诉JVM你别自作聪明给我重排序,我这里不需要排序,我的。前面提到了,volatile的有序性是靠内存屏障禁重排实现的。

  • 对于编译器的重排序,JMM会根据重排序的规则,禁止特定类型的编译器重排序

  • 对于处理器的重排序,Java编译器在生成指令序列的适当位置,插入内存屏障指令,来禁止特定类型的处理器排序

在这里插入图片描述
在这里插入图片描述

5、读写屏障插入策略

读屏障的插入策略:

  • 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障
  • 在每个 volatile 读操作的后面插入一个 LoadStore 屏障

volatile 读操作就是读一个volatile变量

在这里插入图片描述

写屏障的插入策略:

  • 在每个 volatile 写操作的前面插入一个 StoreStore 屏障
  • 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障

在这里插入图片描述

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

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

相关文章

Linux介绍

文章目录 前言一、概述 前言 Linux学习笔记。 一、概述 linux怎么读,不下10种 linux是一个开源、免费的操作系统,其稳定性、安全性、处理多并发已经得到业界的认可,目前很多企业级的项目(c/c/php/python/java/go)都会部署到Linux/unix系统上。 常见的…

联软 IT 安全运维管理软件反序列化漏洞复现

0x01 产品简介 联软科技持续十多年研发的联软IT安全运维管理软件,集网络准入控制、终端安全管理、BYOD设备管理、杀毒管理、服务器安全管理、数据防泄密、反APT攻击等系统于一体,通过一个平台,统一框架,数据集中,实现更…

Android中在google Map 上绘制历史路径

很多的App都会有这种需求,需要把自己的轨迹绘制在地图上来加标一段行踪,使得自己的行程展现出来,通过地图的展示,自己的行程也就一目了然了。 这里利用Google Map 把自己的行程展现出来,注意这里用到了上一章的基础&a…

C语言——写一个简单函数,找两个数中最大者

#include <stdio.h>int max( int a, int b ) { return a>b ? a:b; }int main() { int a, b;printf("输入两个数:\n");scanf("%d %d", &a, &b);printf("max %d\n", max(a, b));return 0; }输出结果&#xff1a;

csdn最新最全面的Jmeter接口测试:jmeter_逻辑控制器_循环控制器

循环控制器 循环次数&#xff1a;设置该控制器下的请求的循环执行次数 永远&#xff1a;勾选上的话&#xff0c;会一直循环&#xff0c;即所谓死循环 注意&#xff1a;如果线程组本身已经设置了循环次数的话&#xff0c;那循环控制元件控制的子节点 的循环次数为线程组设置的…

Rust的Vec优化

本篇是对Rust编程语言17_Rust的Vec优化[1]学习与记录 MiniVec https://crates.io/crates/minivec enum DataWithVec { // tag,uint64,8字节 I32(i32), // 4字节,但需内存对齐到8字节? F64(f64), // 8字节 Bytes(Vec<u8>), // 24字节}fn main()…

浅聊代理(应用部署)

以前很少接触过项目的上线部署&#xff0c; 我对前后端交互的认知还停留在前端一个请求 对应后端一个API 比如后端提供: /api/backend/categories -GET 前端则通过使用ajax或者axios组件去构建http请求&#xff0c; 发送到: https://host:port/api/backend/categories -GET 一、…

安全高效的PostgreSQL数据库迁移解决方案

PostgreSQL数据库是一款高度可扩展的开源数据库系统&#xff0c;支持复杂的查询、事务完整性和多种数据类型&#xff0c;这使得它成为企业中处理大规模和多样化数据需求的理想选择。在很多企业中&#xff0c;PostgreSQL不仅处理大量的交易数据&#xff0c;还支持复杂的数据分析…

Django二转Day03 04

0 cbv执行流程&#xff0c;self问题 path(index/, Myview.as_view()),Myview.as_view() 实例化后返回 变成return Myview.dispatch(request, *args, **kwargs)但是视图函数Myview中没有 dispatch 方法 所以去 父类View中寻找return View.dispatch(request, *args, **kwargs)调用…

Selenium(12):层级定位_通过父元素找到子元素

层级定位 在实际的项目测试中&#xff0c;经常会遇到无法直接定位到需要选取的元素&#xff0c;但是其父元素比较容易定位&#xff0c;通过定位父元素再遍历其子元素选择需要的目标元素&#xff0c;或者需要定位某个元素下所有的子元素。 层级定位的思想是先定位父对象&#xf…

【腾讯云云上实验室】向量数据库+LangChain+LLM搭建智慧辅导系统实践

目录 一、搭建智慧辅导系统——向量数据库实践指南1.1、创建向量数据库并新建集合1.2、使用 TKE 快速部署 ChatGLM1.3、部署 LangChain PyPDFVectorDB等组件1.4、配置知识库语料1.5、基于 VectorDB LLM 的智能辅导助手 二、LLM时代的次世代引擎——向量数据库2.1、向量数据库L…

FastDFS+Nginx - 本地搭建文件服务器同时实现在外远程访问「内网穿透」

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…

ProgrammingError: nan can not be used with MySQL

该错误怎么发生的&#xff1f; 我们先在本地创建测试表&#xff1a; CREATE TABLE users_test (id int NOT NULL AUTO_INCREMENT COMMENT 主键,trade_account varchar(50) DEFAULT NULL COMMENT 交易账号,username varchar(50) DEFAULT NULL,email varchar(100) DEFAULT NULL…

数字系列——数字经济

数字经济是全球经济未来发展方向&#xff0c;正在成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。都知道数字经确实很重要&#xff0c;但有些人还傻傻搞不懂数字经济到底是什么&#xff1f;小编今天就给大家捋一捋。 什么是数字经济&#xff1f; 数字经…

Glove学习笔记

global vectors for word representation B站学习视频 1、LSA与word2vec 我们用我们的见解&#xff0c;构建一个新的模型&#xff0c;Glove&#xff0c;全局向量的词表示&#xff0c;因为这个模型捕捉到全局预料的统计信息。 LSA:全局矩阵分解word2vec&#xff1a;局部上下文…

AI生成的图片有版权了

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 把发到小红书的AI图片搬运到百家号&#xff0c;然后被起诉了! 长知识了&#xff0c;原来AI生成的图片也有版权了&#xff0c;AI生成图片著作权第一案判了&#xff0c;这绝对是一件划时代事情&…

微信小程序真机调试技巧,解决各种疑难杂症

1.在真机上看log 也许你调试的时候&#xff0c;会使用到真机调试或者预览模式或者体验版模式&#xff0c;这些模式都有可能出现意想不到的bug问题&#xff0c;这时候调试模式就非常非常重要了&#xff0c;特别是给领导看的时候&#xff0c;在领导手机上出现bug了&#xff0c;这…

QT 项目中添加文件夹(分类文件)

为了更方便的整理项目的文件&#xff0c;添加文件夹把文件进行分类。 1.首先在项目文件中创建新的文件夹 2.把需要归类的文件放入新建的文件中 3.右键然后选择add..... 4.运行此程序&#xff0c;会报错因为文件路径改变了&#xff0c;需要在.pro中修改路径 注意事项 文件夹内部…

NSSCTF第14页(2)

[UUCTF 2022 新生赛]ezpop 提示说看看反序列化字符串逃逸 PHP反序列化字符串逃逸_php反序列化逃逸-CSDN博客 php反序列化字符逃逸_php反序列化逃逸_Leekos的博客-CSDN博客 buuctf刷题9 (反序列化逃逸&shtml-SSI远程命令执行&idna与utf-8编码漏洞)_extract($_post);…

码云配置遇到秘钥不正确

你这个就是秘钥没有和git绑定&#xff0c; 需要 git config --global user.name "你的用户名随便写" git config --global user.email "你的邮箱"