浅谈volatile
volatile有三个特性:
(1)可见性
(2)不保证原子性
(3)禁止指令重排
下面我们一一介绍
(一)可见性
volatile的可见性是说共享变量只要修改,就可以被其他线程感知到,这个是怎么做到的呢?
这得从虚拟机的内存模型说起,这里直接引用《深入理解java虚拟机》书中原话:JAVA内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存保存了被该线程使用的变量的主内存副本,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不是直接读写主内存中的数据。
所以对于多线程场景的共享变量值的传递是需要通过主内存进行。也就是线程A对共享变量tmp(没有使用volatile)修改后,线程B不一定能马上感知到。需要等线程A中的工作内存同步到主内存中,线程B才能感知到。而volatile则会保证变量只要修改就会马上同步到主内存中,并且线程读取变量时也是会先从主内存中刷新变量值,以此来保证了变量的可见性。
(二)不保证原子性
对于volatile变量,虽然线程能马上看见它的值改变,并不能保证它的运算操作是原子的,比如:
public class TestVolatile {public static volatile int count = 0;public static CountDownLatch countDownLatch = new CountDownLatch(5);public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {for (int j = 0; j < 10000; j++) {count++;}countDownLatch.countDown();}).start();}try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count:" + count);}
}
上面这个例子执行后,如果是原子性的,那么count的值应该是50000。但实际远小于50000

因为i++本身就不是原子步骤,它实际上是i=i+1.它需要先获取i的值,然后再加1,然后再赋值给i。两个线程A,B同时获取i的值为3,然后线程A将其加1后的值4赋值给主内存。线程B这时也完成了计算同样赋值给主内存也是4。这是计算结果就少了1。
(3)禁止指令重排
指令重排是编译器在运行时在保证单个线程内结果不变的情况下,基于性能优化考虑对指令进行重新排序
这个先举个例子:
public static boolean initFlag = false;public static void main(String[] args) {new Thread(() -> {// 初始化完成后再设置标志位,另一个线程感知到标志位改变后,开始执行它的业务doSomeInit();initFlag = true;}).start();new Thread(() -> {while (!initFlag) {// 还没初始化则等待try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}// 认为初始化已经完成,执行它的业务}).start();}
上面这段代码并不能实际运行出我们想要的效果(因为指令重排是虚拟机编译器内部逻辑,没想到一个确实可行的例子能够触发指令重排),只是简单代替我们经常可能会出现的业务场景,一个线程等待另一个线程完成初始化逻辑后,才开始启动自己的业务逻辑。上面initFlag没有标识volatile。那么initFlag赋值操作可能会因为指令重排,先于doSomeInit初始化逻辑先完成,这样就可能导致我们的业务功能有问题。因为对于单个线程来说,虚拟机认为initFlag提前执行并没有改变所在线程的结果。所以这里需要给initFlag加上volatile标识,告诉虚拟机,这里不能进行指令重排,在initFlag操作之前的代码,也必须先于initFlag操作之前执行
相关文章:
浅谈volatile
volatile有三个特性: (1)可见性 (2)不保证原子性 (3)禁止指令重排 下面我们一一介绍 (一)可见性 volatile的可见性是说共享变量只要修改,就可以被其他线…...
Python3 爬虫 Scrapy的使用
安装完成Scrapy以后,可以使用Scrapy自带的命令来创建一个工程模板。 一、创建项目 使用Scrapy创建工程的命令为: scrapy startproject <工程名> 例如,创建一个抓取百度的Scrapy项目,可以将命令写为: scrapy s…...
多线程篇-4--重点概念1(volatile,Synchronized,内存屏障,MESI协议)
一、volatile (1)、简述 volatile是java提供的一个关键字,英文意思为不稳定的。 可以保障被声明对象的可见性和一定程度上的有序性,但不能保证操作的原子性。 当一个变量被声明为volatile时,意味着该变量的值会直接从…...
本地学习axios源码-如何在本地打印axios里面的信息
1. 下载axios到本地 git clone https://github.com/axios/axios.git 2. 下载react项目, 用vite按照提示命令配置一下vite react ts项目 npm create vite my-vue-app --template react 3. 下载koa, 搭建一个axios请求地址的服务端 a.初始化package.json mkdir koa-server…...
1、SpringBoo中Mybatis多数据源动态切换
我们以一个实例来详细说明一下如何在SpringBoot中动态切换MyBatis的数据源。 一、需求 1、用户可以界面新增数据源相关信息,提交后,保存到数据库 2、保存后的数据源需要动态生效,并且可以由用户动态切换选择使用哪个数据源 3、数据库保存了多个数据源的相关记录后,要求…...
【浏览器】缓存与存储
我是目录 浏览器缓存为什么需要浏览器缓存?对浏览器的缓存机制的理解协商缓存和强缓存的区别强缓存协商缓存 点击刷新按钮或者按 F5、按 CtrlF5 (强制刷新)、地址栏回车有什么区别? 浏览器本地存储前端储存的方式有哪些࿱…...
积鼎科技携手西北工业大学动力与能源学院共建复杂多相流仿真联合实验室
11月26日,复杂多相流仿真联合实验室揭牌仪式及技术研讨活动在西北工业大学动力与能源学院成功举办。复杂多相流仿真联合实验室是由西北工业大学动力与能源学院牵头,携手上海积鼎信息科技有限公司与三航铸剑(西安)科技发展有限公司…...
5. langgraph实现高级RAG (Adaptive RAG)
1. 数据准备 from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import WebBaseLoader from langchain_community.vectorstores import Chromaurls ["https://lilianweng.github.io/posts/2023-06-23-age…...
Postman设置接口关联,实现参数化
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 postman设置接口关联 在实际的接口测试中,后一个接口经常需要用到前一个接口返回的结果, 从而让后一个接口能正常执行,这…...
代码随想录day02--链表
移除链表元素 题目 地址:https://leetcode.cn/problems/remove-linked-list-elements/description/ 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 思路是使用虚拟节点的…...
杰发科技AC7803——不同晶振频率时钟的配置
计算公式 PLL_POSDIV [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62] PLL_PREDIV_1 1 2 4 USE_XTAL 24M SYSCLK_FREQ 64M SYSCLK_DIVIDER 1 VCO USE_XTAL*…...
ArcGIS栅格影像裁剪工具
1、前言 在最近的栅格转矢量处理过程中,发现二值化栅格规模太大,3601*3601,并且其中的面元太过细碎,通过arcgis直接栅格转面有将近几十万的要素,拿这样的栅格数据直接运行代码,发现速度很慢还难以执行出来结…...
【查询目录】.NET开源 ORM 框架 SqlSugar 系列
.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...
docker快速安装zookeeper
一、拉取镜像 docker pull zookeeper:3.9.3 二、启动zookeeper docker run --restartalways -d --name zookeeper -p 2181:2181 -v /etc/localtime:/etc/localtime zookeeper:3.9.3 如果需要挂载zookeeper文件及目录,则参数增加: -v /mydata/zookeeper/d…...
MySQL中如何减少回表
在MySQL中,回表是指在使用非聚集索引进行查询时,如果需要获取的数据不在索引页中,就需要根据索引页中的指针返回到数据表中查找实际数据行的过程。这个过程会增加额外的磁盘I/O操作,降低查询性能,特别是在查询大量数据…...
初始Python篇(7)—— 正则表达式
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: Python 目录 正则表达式的概念 正则表达式的组成 元字符 限定符 其他字符 正则表达式的使用 正则表达式的常见操作方法 match方法的…...
洛谷P1443 马的遍历
简单的bfs 题目链接 P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 有一个 nm 的棋盘,在某个点(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。 输入格式 输入只有一行四个整数,分别为 n…...
代理IP地址的含义与设置指南
在数字化时代,互联网已经成为我们日常生活不可或缺的一部分。然而,在享受互联网带来的便利的同时,我们也面临着隐私泄露、访问限制等问题。代理IP地址作为一种有效的网络工具,能够帮助我们解决这些问题。本文将详细介绍代理IP地址…...
Vue--------导航守卫(全局,组件,路由独享)
全局导航守卫 beforeEach 全局前置守卫 afterEach 全局后置守卫 路由独享守卫 beforeEnter 路由独享守卫 组件导航守卫 beforeRouteEnter 进入组件前 beforeRouteUpdate 路由改变但是组件复调用 beforeRouteLeave 离开组件之前 执行顺…...
ElasticSearch7.x入门教程之全文搜索(七)
文章目录 前言一、多条件查询:bool query二、更加精准查询:dis_max query总结 前言 这里再接着上一篇文章继续记录。非常感谢江南一点雨松哥的文章。 欢迎大家去查看,地址:http://www.javaboy.org 一、多条件查询:boo…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
