深入Volatile
深入Volatile
1、变量不可见性:
1.1多线程下变量的不可见性
直接上代码
/*** @author yourkin666* @date 2024/08/12/16:12* @description*/
public class h1 {public static void main(String[] args) {MyClass myClass = new MyClass();myClass.start();while (true) {if (myClass.isFlag()) {System.out.println("circle!!!");}}}}
class MyClass extends Thread{private boolean flag = false;@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}flag = true;System.out.println("flag = "+ flag);}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}
子线程从主内存中读取数据放到工作内存,将flag修改为true,但是此时flag的值还没有写回主内存,所以主线程读取的flag依然是false
当子线程将flag值写回主内存后,main函数里面的while(true)调用的是系统比较底层的代码,速度较快,没时间再去主存中读取flag
所以while(true)读取到的值一直是false(当然主线程可能在一个时刻去主内存读取最新的值,我们无法控制)
1.2变量不可见性内存语义
JMM(java内存模型)是针对于Java并发编程的模型
JMM规范:
- 所有共享变量(实例变量和类变量)都存在于主内存,不包含局部变量,因为局部变量是线程私有
- 每一个线程都还有自己的工作内存,在工作内存中,保存了一份共享变量的副本
- 线程对变量的所有操作都在工作内存中完成
- 不同线程的工作内存是隔离的
可见性问题的原因:
- 所有共享变量都存在于主内存,每个线程都有自己的工作内存,而且线程读写共享数据也是通过本地内存交换的,所以导致了可见性问题
1.3变量不可见行的解决方案
第一种是加锁
第二种是使用volatile关键字
加锁:
while (true) {synchronized (myClass) {if (myClass.isFlag()) {System.out.println("circle!!!");}}
}
线程进入synchronized代码块前后的执行过程:
- 线程获得锁
- 清空工作内存
- 从主内存拷贝共享变量的最新值
- 执行代码块
- 将修改后的副本的值刷新回主内存中
- 线程释放锁
volatile关键字:
private volatile boolean flag = false;
工作原理:
- 子线程从主内存读取数据放在工作内存,修改flag值后,还没有写回主内存
- 此时主线程读取到了flag值为false
- 当子线程将flag写回主线程后,就失效工作内存中的拷贝值
- 再次对flag进行操作时,线程会从主内存读取最新值,放入工作内存
volatile关键字保证不同线程对共享变量操作的可见性,也就是一个线程修改了volatile修饰的变量,当此变量写回主内存后,其他线程可以立即看到最新值
volatile修饰的变量可以在多线程并发修改下,实现线程间变量的可见性
2、volatile其他特性:
1.volatile不保证原子性
/*** @author yourkin666* @date 2024/08/12/16:12* @description*/
public class h1 {public static void main(String[] args) {Runnable myClass = new MyClass();for (int i = 1; i <= 100 ; i++) {new Thread(myClass).start();}}
}
class MyClass implements Runnable{private volatile int count = 0;@Overridepublic void run() {for (int i = 1; i <= 10000; i++) {count++;System.out.println("count >>>>" + count);}}
}
count++是非原子操作,起码可以被分为三步
在多线程环境下,volatile修饰的变量也是线程不安全的
要保证数据的安全性,还得是锁,或者原子类
原子类:
java.util.concurrent.atomic(Atomic包)
这个包中的原子操作类提供一种用法简单、性能高效、 线程安全地更新一个变量的方式
AtomicInteger:
原子型Integer ,可以实现原子更新操作:
public AtomicInter()
初始化一个默认值为0的原子型Integet
public AtomicInter(int initialValue)
初始化一个指定值的原子型Integet
int get()
获取值
int getAndIncrement()
以原子方式将当前值加1,返回自增前的值
int incrementAndget()
以原子方式将当前值加1,返回自增后的值
int addAndGet(int data)
以原子方式将输入的值与AtomicInteger里的value相加,并返回结果
int getAndSet(int value)
以原子方式设置newValue的值,并返回旧值
2.禁止指令重排序
为了提升程序的运行效率,编译器和处理器常常对指令进行重排序
使用volatile可以禁止指令重排序,从而修正重排序可能带来的并发安全问题
3、volatile内存语义:
volatile写读建立的happens - before关系:
从JDK5开始,提出happens - before概念,来阐述操作之间的内存可见性。
如果一个操作的结果需要对另一个操作可见,那么这两个操作之间必须存在happens - before关系。(这里提到的两个操作既可以是在一个线程中,也可以是不同线程之间
happens - before规则:
-
单线程规则
同一个线程中,前面的所有写操作对后面的操作可见
-
锁规则(synchronized、Lock等)
如果线程1解锁了monitor A ,接着线程2锁定了A,那么,线程1解锁A之前的写操作都对线程2可见
-
volatile变量规则
如果线程1写入volatile变量v(临界资源),接着线程2读取了v,那么,线程1写入v及之前的写操作都是对线程2可见的
-
传递性
A对B可见,B对C可见,那么A对C可见
-
start()规则
假设线程A在执行过程中,通过执行ThreadB.start()来启动线程B,那么线程A在线程B执行前对共享变量的修改,是对线程B可见(线程B启动后的,线程A对变量的修改,线程B就未必看得到了
-
join()规则
线程t1写入的所有变量,在任意其他线程t2调用t1.join(),或者t1.isAlive()成功返回后,都对t2可见
4、volatile相关面试题:
相关文章:
深入Volatile
深入Volatile 1、变量不可见性: 1.1多线程下变量的不可见性 直接上代码 /*** author yourkin666* date 2024/08/12/16:12* description*/ public class h1 {public static void main(String[] args) {MyClass myClass new MyClass();myClass.start();while (tr…...
数据结构 ——— 顺序表oj题:编写函数,合并两个有序数组
目录 题目要求 代码实现 题目要求 nums1 和 nums2 是两个升序的整型数组,另外有两个整数 m 和 n 分别代表 nums1 和 nums2 中的元素个数 要求合并 nusm2 到nums1 中,使合并后的 nums1 同样按升序顺序排列 最终,合并后的数组不应由函数返…...
Proto文件相关知识
百度Apollo的数据结构常用proto文件来定义, proto文件允许你以类似于C结构体或类的方式定义数据结构。你可以在这个文件中定义简单数据类型、枚举、消息类型等。 基于proto文件,Protocol Buffers编译器(protoc)可以自动生成对应的…...
k8s的控制节点不能访问node节点容器的ip地址
master控制node服务器添加容器后,访问不了该node服务器容器的ip,只能在node服务器访问 排查后发现是k8s的master服务器和node节点的网址网段和k8s初始化时提示的ip网段不一致 我之前是192.168.137.50, 实际上master主机期望的是192.168.1.50 解决方案: 1.删除服务器后重建ma…...
鸿蒙OpenHarmony
开源鸿蒙系统编译指南 Ubuntu编译环境配置第一步:Shell 改 Bash第二步:安装Git和安装pip3工具第三步:远程仓配置第四步:拉取代码第五步:安装编译环境第六步:本地编译源码 Windows开发环境配置第一步&#x…...
把白底照片变蓝色用什么软件免费 批量更换证件照底色怎么弄
作为专业的修图师,有时候也会接手证件照修图和换底色工作,这种情况下,需要换底色的照片也许达到上百张。为了提高工作效率,一般需要批量快速修图,那么使用什么软件工具能够给各式不同的照片批量更换背景色呢࿱…...
Spring之生成Bean
Bean的生命周期:实例化->属性填充->初始化->销毁 核心入口方法:finishBeanFactoryInitialization-->preInstantiateSingletons DefaultListableBeanFactory#preInstantiateSingletons用于实例化非懒加载的bean。 1.preInstantiateSinglet…...
笔记整理—linux进程部分(6)进程间通信、alarm和pause
两个进程间通信可能是任何两个进程间的通信(IPC)。同一个进程是在同一块地址空间中的,在不同的函数与文件以变量进程传递,也可通过形参传递。2个不同进程处于不同的地址空间,要互相通信有难度(内存隔离的原…...
Java网络通信—UDP
0.小记 1.udp通信不需要建立socket管道,一边只管发,一边只管收 2.客户端:将数据(byte)打包成包裹(DatagramPacket),写上地址(IP端口),通过快递站&…...
k8s架构,从clusterIP到光电半导体,再从clusterIP到企业管理
clusterIP作为k8s中的服务, 也是其他三个服务的基础 ~]$ kubectl create service clusterip externalname loadbalancer nodeport 客户端的流量到service service分发给pod,pod由控制器自动部署,自动维护 那么问题是service的可用…...
vue框架和uniapp框架区别
文章目录 vue框架和uniapp框架区别一、引言二、Vue.js 概述1、Vue.js 简介1.1、特点 2、适用场景 三、Uni-app 概述1、Uni-app 简介1.1、特点 2、适用场景 四、区别与比较1、跨平台能力2、开发体验3、性能优化4、社区和支持 五、总结 vue框架和uniapp框架区别 一、引言 在前端…...
828华为云征文 | 华为云Flexus云服务器X实例搭建Zabbix网络设备监视系统(Ubuntu服务器运维)
前言 Flexus X实例内嵌智能应用调优算法,性能强悍,基础模式GeekBench单核及多核跑分可达同规格独享型实例的1.6倍,性能模式更是超越多系列旗舰型云主机,为企业业务提供强劲动力。 💼 Flexus X Zabbix:打造…...
JAVA基础-线程(Thread)、多线程(Multi-threaded)
1、知识铺垫 要想了解什么是线程,首先要搞明白线程与进程的区别,并行与并发的区别 1.1 线程与进程 进程:是指⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间,⼀个应⽤程序可以同时运⾏多个进程;…...
hystrix微服务部署
目录 一.启动nacos和redis 1.查看是否有nacos和redis 二.开始项目 1.hystrix1工程(修改一下工程的注册名字) 2.运行登录nacos网站查看运行效果(默认密码nacos,nacos) 3.开启第二个项目 hystrix2工程 4.关闭第二个项目 hyst…...
使用百度文心智能体创建多风格表情包设计助手
文章目录 一、智能定制,个性飞扬二、多元风格,创意无限 百度文心智能体平台为你开启。百度文心智能体平台,创建属于自己的智能体应用。百度文心智能体平台是百度旗下的智能AI平台,集成了先进的自然语言处理技术和人工智能技术&…...
【嵌入式裸机开发】智能家居入门3(MQTT服务器、MQTT协议、微信小程序、STM32)
前面已经写了两篇博客关于智能家居的,服务器全都是使用ONENET中国移动,他最大的优点就是作为数据收发的中转站是免费的。本篇使用专门适配MQTT协议的MQTT服务器,有公用的,也可以自己搭建 前言一、项目总览二、总体流程分析1、了解…...
css的背景background属性
CSS的background属性是一个简写属性,它允许你同时设置元素的多个背景相关的子属性。使用这个属性可以简化代码,使其更加清晰和易于维护。background属性可以设置不同的子属性。 background子属性 定义背景颜色 使用background-color属性 格式&#x…...
Cypress自动化测试实战:构建高效的前端测试体系
在快速迭代的软件开发环境中,前端自动化测试是保证代码质量和用户体验的重要手段。Cypress作为一款功能强大的前端自动化测试工具,凭借其丰富的特性、直观的API和高效的测试执行速度,赢得了众多开发者和测试团队的青睐。本文将深入探讨Cypres…...
【YOLO学习】YOLOv2详解
文章目录 1. 概述2. Better2.1 Batch Normalization(批归一化)2.2 High Resolution Classifier(高分辨率分类器)2.3 Convolutional With Anchor Boxes(带有Anchor Boxes的卷积)2.4 Dimension Clusters&…...
windows 录音编码为flv格式时,pcm采样格式
这里使用的是0x3e,转换为二进制: 0 0 1 1 1 1 1 0 前四个字节为3,表示Linear Pcm, 后4个字节1 1 1 0 表示44100HZ采样, 16个bit,单声道。 故,windows 音频采样不支持48000HZ频率...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...
AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...
