【Java】volatile

news2025/7/24 3:58:59

一、volatile

volatileJava虚拟机提供的轻量级的同步机制,它有3个特性:
1)保证可见性
2)不保证原子性
3)禁止指令重排

  • 一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中
  • 一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量

所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取

1. 可见性

保证不同线程对这个变量进行操作时的可见性,即变量一旦改变所有线程立即可见 。

使用volatile修饰共享变量,被volatile修改的变量有以下特点:

  1. 线程中读取的时候,每次读取都会去主内存中读取共享变量最新的值,然后将其复制到工作内存
  2. 线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存

volatile变量的读写过程分析:

use(使用)一个变量的时候必需load(载入),要载入的时候必需从主内存read(读取)这样就解决了读的可见性。写操作是把assign(赋值)和store(存储)做了关联(在assign(赋值)后必需store(存储)),store(存储)后write(写入)。 也就是做到了给一个变量赋值的时候一串关联指令直接把变量值写到主内存。 就这样通过用的时候直接从主内存取,在赋值到直接写回主内存做到了内存可见性。

2. 无原子性

原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。

多线程环境下,“数据计算”和“数据赋值”操作可能多次出现,即操作非原子。若数据在加载之后,若主内存count变量发生修改之后,由于线程工作内存中的值在此前已经加载,从而不会对变更操作做出相应变化,即私有内存和公共内存中变量不同步,进而导致数据不一致。

对于volatile变量,JVM只是保证从主内存加载到线程工作内存的值是最新的,也就是数据加载时是最新的。由此可见volatile解决的是变量读时的可见性问题,但无法保证原子性,对于多线程修改共享变量的场景必须使用加锁同步。

i++为例,不具备原子性,该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分3步完成。
在这里插入图片描述

如果第二个线程在第一个线程读取旧值写回新值期间(上图所指三步期间)读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败,因此对于add方法必须使用synchronized修饰,以便保证线程安全。

volatile变量的读写过程分析:

read-load-useassign-store-write成为了两个不可分割的原子操作,但是在useassign之间依然有极小的一段真空期,有可能变量会被其他线程读取,导致写丢失一次

3. 指令禁重排

重排序:

是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序。

  • 不存在数据依赖关系,可以重排序;
  • 存在数据依赖关系,禁止重排序 。

但重排后的指令绝对不能改变原有的串行语义。

数据依赖性:若两个操作访问同一变量,且这两个操作中有一个为写操作,此时两操作间就存在数据依赖性

重排序的分类和执行流程 :

  1. 编译器优化的重排序:编译器在不改变单线程串行语义的前提下,可以重新调整指令的执行顺序

  2. 指令级并行的重排序:处理器使用指令级并行技术来将多条指令重叠执行,若不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序

  3. 内存系统的重排序:由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是乱序执行

在这里插入图片描述

volatile有关禁重排的行为

  1. 当第一个操作为volatile读时,不论第二个操作是什么,都不能重排序。这个操作保证了volatile读之后的操作不会被重排到volatile读之前(volatile读之后的操作,都禁止重排序到volatile之前)

  2. 当第二个操作为volatile写时,不论第一个操作是什么,都不能重排序。这个操作保证了volatile写之前的操作不会被重排到volatile写之后 (volatile写之前的操作,都禁止重排序到volatile之后)

  3. 当第一个操作为volatile写时,第二个操作为volatile读时,不能重排。(volatile写之后volatile读,禁止重排序的)

内存屏障四大指令插入情况

  1. 在每个volatile操作的前面插入一个StoreStore屏障,保证在volatile之前,其前面的所有普通操作都已经刷新到主内存中。

  2. 在每个volatile操作的后面插入一个StoreLoad屏障,避免volatile与后面可能有的volatile读/写操作重排序

  3. 在每个volatile操作的后面插入一个LoadLoad屏障,禁止处理器把上面的volatile与下面的普通重排序。

  4. 在每个volatile操作的后面插入一个LoadStore屏障,禁止处理器把上面的volatile与下面的普通重排序。

二、如何正确使用volatile

由于volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍然要通过加锁(使用synchronizedjava.util.concurrent中的原子类)来保证原子性:

  • 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
  • 变量不需要与其他的状态变量共同参与不变约束。

1. 单一赋值可以,但是含复合运算赋值不可以(i++之类)

volatile int a = 10;

2. 状态标志,判断业务是否结束

使用:作为一个布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或任务结束

理由:状态标志并不依赖于程序内任何其他状态,且通常只有一种状态转换

例子:判断业务是否结束

 volatile boolean flag = false 

3. 开销较低的读,写锁策略

使用:当读远多于写,结合使用内部锁和volatile变量来成少同步的开销 。

理由

  • 利用volatile保证读取操作的可见性;
  • 利用synchronized保证复合操作的原子性。

4. DCL双端锁的发布

隐患:多线程环境下,由于重排序,该对象可能还未完成初始化就被其他线程读取

修正方法1:加volatile(也即正确的DCL双端锁)

原理:利用volatile, 禁止"初始化对象"和"设置singleton指向内存空间"的重排序

修正方法2:静态内部类

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

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

相关文章

openEuler部署Ceph集群(块存储)

openEuler部署Ceph集群1 目标2 环境2.1 服务器信息2.2 软件信息3 部署流程3.1 获取系统镜像3.2 创建虚拟机3.3 配置虚拟机3.3.1 配置互信3.3.2 关闭防火墙3.3.3 配置免密登录3.3.4 配置NTP3.3.4.1 安装NTP服务3.3.4.2 配置NTP服务端3.3.4.3 配置NTP客户端3.3.4.4 启动NTP服务3.…

pyqt5通过CANoe COM Server来操作CANoe仿真工程

文章目录前言一、COM接口技术二、UI界面设计三、功能实现四、工程运行测试前言 继续学习《CANoe开发从入门到精通》。 今天在《CANoe仿真工程开发》的基础上,开发实现pyqt5应用程序来操控CANoe工程。 一、COM接口技术 COM(Component Object Model&…

Linux基础命令-find搜索文件位置

文章目录 find 命令介绍 语法格式 命令基本参数 参考实例 1)在root/data目录下搜索*.txt的文件名 2)搜索一天以内最后修改时间的文件;并将文件删除 3)搜索777权限的文件 4)搜索一天之前变动的文件复制到test…

不懂什么是智慧工厂,看这篇文章就够了!

一、智慧工厂是什么? 一直以来,自动化在某种程度上始终是工厂的一部分,甚至高水平的自动化也非新生事物。然而,“自动化”一词通常表示单一且独立的任务或流程的执行。过去,机器自行“决策”的情况往往是以自动化为基…

【基础篇】9 # 排序:冒泡排序(Bubble Sort)、插入排序(Insertion Sort)、选择排序(Selection Sort)

说明 【数据结构与算法之美】专栏学习笔记 如何分析一个排序算法? 1、排序算法的执行效率 最好情况、最坏情况、平均情况时间复杂度时间复杂度的系数、常数 、低阶比较次数和交换(或移动)次数 2、排序算法的内存消耗 3、排序算法的稳定…

Fabric.js使用说明Part 2

目录一、Fabric.js使用说明Part 1Fabric.js简介 开始方法事件canvas常用属性对象属性图层层级操作复制和粘贴二、Fabric.js使用说明Part 2锁定拖拽和缩放画布分组动画图像滤镜渐变右键菜单删除三、Fabric.js使用说明Part 3自由绘画绘制背景图片绘制文本绘制线和路径一、锁定Fab…

传统豪华品牌引领?智能座舱进入「沉浸式娱乐体验」新周期

智能座舱正在进入硬件定型、软件(功能)升级以及多应用融合的新周期。 高工智能汽车研究院监测数据显示,2022年中国市场(不含进出口)乘用车搭载智能数字座舱(大屏语音车联网OTA)前装标配交付795…

【死磕数据库专栏启动】在CentOS7中安装 MySQL5.7版本实战

文章目录前言实验环境一. 安装MySQL1.1 配置yum源1.2 安装之前的环境检查1.3 下载MySQL的包1.4 开始使用yum安装1.5 启动并测试二. 设置新密码并重新启动2.1 设置新密码2.2 重新登录测试总结前言 学习MySQL是一件比较枯燥的事情,学习开始之前要先安装MySQL数据库&a…

【Linux修炼】14.磁盘结构/文件系统/软硬链接/动静态库

每一个不曾起舞的日子,都是对生命的辜负。 磁盘结构/文件系统/软硬链接/动静态库前言一.磁盘结构1.1 磁盘的物理结构1.2 磁盘的存储结构1.3 磁盘的逻辑结构二.理解文件系统2.1 对IO单位的优化2.2 磁盘分区与分组2.3 分组的管理方法2.4 文件操作三.软硬链接3.1理解硬…

测试4年裸辞失业,面试17k的测试岗被按在地上摩擦,结局让我崩溃大哭...

作为IT行业的大热岗位——软件测试,只要你付出了,就会有回报。说它作为IT热门岗位之一是完全不虚的。可能很多人回说软件测试是吃青春饭的,但放眼望去,哪个工作不是这样的呢?会有哪家公司愿意养一些闲人呢?…

「smardaten」上架钉钉应用中心!让进步再一次发生

使用钉钉的团队小伙伴们,smardaten给您送来福利啦~为了给更多团队提供更优质的应用开发体验,方便用户在线、快速使用无代码,数睿数据近期在【钉钉应用中心】发布smardaten在线版本。继与华为云、亚马逊云建立战略合作之后,smardat…

微信小程序实现分享到朋友圈的功能

分享朋友圈官方API:分享到朋友圈 1、分享到朋友圈接口设置事项 2、onShareTimeline()注意事项 3、分享朋友圈后,测试发现,没有数据请求。 用户在朋友圈打开分享的小程序页面,并不会真正打开小程序,而是进入一个“小程…

浏览器缓存策略

先走强缓存,再走协商缓存 强缓存 不发送请求,直接使用缓存的内容 状态码200 当前会话没有关闭的话就是走memory cache,否则就是disk cache 由响应头的 Pragma(逐渐废弃,优先级最高),catch-…

LeetCode 817. 链表组件

LeetCode 817. 链表组件 难度:middle\color{orange}{middle}middle 题目描述 给定链表头结点 headheadhead,该链表上的每个结点都有一个 唯一的整型值 。同时给定列表 numsnumsnums,该列表是上述链表中整型值的一个子集。 返回列表 numsnu…

自动驾驶仿真:ECU TEST 、VTD、VERISTAND连接配置

文章目录一、ECU TEST 连接配置简介二、TBC配置 test bench configuration三、TCF配置 test configuration提示:以下是本篇文章正文内容,下面案例可供参考 一、ECU TEST 连接配置简介 1、ECU TEST(简称ET),用于HIL仿…

MySQL tinyint(1) 、int(32) 与 varchar(255) 长度含义不同

MySQL tinyint(1) 、int(32) 与 varchar(255) 长度含义不同 发现 tinyint(1),int(32) 和 varchar(255) 这里面的数字的含义是不同的。 先说数字类型 tinyint 和 int 等 他们能存储的字节大小是与类型绑定的,即定义了 tinyint 或者 int 就确定了能存储…

【C++的OpenCV】第六课-OpenCV图像常用操作(三):OpenCV的图像的侵蚀和扩张

让我们继续一、图像的侵蚀和扩张1.1 侵蚀1.1.1 函数原型1.1.2 侵蚀的效果1.1.3 关于侵蚀的解释1.2 扩张1.2.1 函数原型1.2.2 扩张的效果二、实例一、图像的侵蚀和扩张 本章节中我们将会学习到: cv::erode() 函数详情cv::dilate() 函数详情 两个函数的基本使用方法…

java 接口 详解

目录 一、概述 1.介绍 : 2.定义 : 二、特点 1.接口成员变量的特点 : 2.接口成员方法的特点 : 3.接口构造方法的特点 : 4.接口创建对象的特点 : 5.接口继承关系的特点 : 三、应用 : 1.情景 : 2.多态 : ①多态的传递性 : ②关于接口的多态参数和多态…

Android ION 相关信息查看方法

目录 查看DMA buffer 信息 查看ion buffer 的总体分配情况 分配的ION buffer 都会设置为DMA buffer,以fd的形式交给使用方, 如app, camera等。 查看ion 的使用情况,可以查看ion 的各个heap分配情况, 也可以从DMA buffer 入手查…

勒索软件BlackByte出现新变种,系Go语言编写

BlackByte 是一个提供勒索软件即服务(RaaS)的攻击组织,自从 2021 年 7 月以来一直保持活跃。最初,BlackByte 使用 C# 开发,最近攻击者使用 Go 重写了恶意软件。FBI 也已经发布公告披露 BlackByte 已经攻击了许多公司&a…