并发编程volatile精解

多线程下变量的不可见性

在多线程并发执行的情况下,多个线程修改共享的成员变量,会出现一个线程修改了共享变量的值后,另一个线程不能直接看到该线程修改后的变量最新值。(多线程下修改共享变量会出现变量修改值后的不可见性)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可见性问题的原因
所有共享变量存在于主内存中,每个线程由自己的本地内存,而且线程读写共享数据也是通过本地内存交换的,所以才导致了可见性问题。
解决方法
1、加锁 2、使用volatile关键字(底层实现原理是内存屏障)【并发编程】volatile
在这里插入图片描述
在这里插入图片描述
volatile修改的变量可以在多线程并发修改下,实现线程间变量的可见性
在这里插入图片描述

在这里插入图片描述
在Java中,volatile 关键字确实不保证复合操作的原子性,但是对于long和double类型的变量,volatile 修饰的读写操作是原子的。
在这里插入图片描述
在多线程环境中,vulatile关键字可以保证共享数据的可见性,但是不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是不安全的)
要保证数据的安全性,还需要使用锁机制。
在这里插入图片描述
什么是重排序
为了提高性能,编译器和处理器常常会对既定的代码执行顺序进行指令重排序。
在这里插入图片描述

好处
重排序可以提高处理的速度。
在这里插入图片描述
使用volatile可以禁止指令的重排序,从而修正重排序可能带来的并发安全问题。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(7)线程中断规则:对线程 interrupt 方法的调用,happens-before 被中断线程的代码检测到中断事件的发生。
(8)对象终结规则:一个对象的初始化完成,happens-before 它的 finalize() 方法的开始。

在这里插入图片描述

long和double的原子性

在java中,long和double都是8个字节共64位(一个字节=8bit),那么如果是一个32位的系统,读写long或double的 变量时会涉及到原子性问题,因为32位的系统要读完一个64位的变量,需要分两步执行,每次读取32位,这样就对double和long变量的赋值就会出现问题: 如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。

public class LongTest09 implements Runnable{ private volatile static long aLong = 0; private volatile long value;public LongTest09(long value) { this.setValue(value);}public long getValue() { return value;}public void setValue(long value) { this.value = value;}@Overridepublic void run() { int i = 0;while (i < 100000) {LongTest09.aLong = this.getValue(); i++;// 赋值操作!!long temp = LongTest09.aLong; // 取出值操作!!!if (temp != 1L && temp != -1L) {System.out.println("出现错误结果" + temp); System.exit(0);} }System.out.println("运行正确"); }public static void main(String[] args) throws InterruptedException { // 获取并打印当前JVM是32位还是64位的String sysNum = System.getProperty("sun.arch.data.model"); System.out.println("系统的位数:"+sysNum);LongTest09 t1 = new LongTest09(1); LongTest09 t2 = new LongTest09(-1); Thread T1 = new Thread(t1);Thread T2 = new Thread(t2); T1.start();T2.start(); T1.join(); T2.join(); }
}

上面的代码在32位环境和64位环境执行的结果是不一样的: 32位环境:出现错误结果 原因:32位环境无法一次读取long类型数据,多线程环境下对Long变量的读写是不完整的,导致temp变量既不等于1也不等于-1。出现了long和double读写的原子性问题了。 64位环境:运行正确
如果是在64位的系统中,那么对64位的long和double的读写都是原子操作的。即可以以一次性读写long或double的整个64bit。如果在32位的JVM上,long和double就不是原子性操作了。

解决办法

  1. 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。
  2. 如果使用volatile修饰long和double,那么其读写都是原子操作
  3. 在实现JVM时,可以自由选择是否把读写long和double作为原子操作;
  4. java中对于long和double类型的写操作不是原子操作,而是分成了两个32位的写操作。读操作是否也分成了两 个32位的读呢?在JSR-133之前的规范中,读也是分成了两个32位的读,但是从JSR-133规范开始,即JDK5开始,读操作也都具有原子性;
  5. java中对于其他类型的读写操作都是原子操作(除long和double类型以外);
  6. 对于引用类型的读写操作都是原子操作,无论引用类型的实际类型是32位的值还是64位的值;
  7. Java商业虚拟机已经解决了long和double的读写操作的原子性。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    双重检查锁
    双重检查锁(double checked locking)是对上述问题的一种优化。先判断对象是否已经被初始化,再决定要不要加锁。
public class Singleton {
//静态属性,volatile保证可见性和禁止指令的重排序private  volatile static Singleton uniqueSingleton;private Singleton() {}public Singleton getInstance() {//第一重检查锁定if (null == uniqueSingleton) {//同步代码块synchronized (Singleton.class) {//第二重检查锁定if (null == uniqueSingleton) {//注意:非原子操作uniqueSingleton = new Singleton();   // error}}}return uniqueSingleton;}
}
  • 检查变量是否被初始化(不去获得锁),如果已被初始化则立即返回。
  • 获取锁。
  • 再次检查变量是否已经被初始化,如果还没被初始化就初始化一个对象。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

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

public class UseVolatile1 implements Runnable { volatile boolean flag = false;AtomicInteger realA = new AtomicInteger(); @Overridepublic void run() {for (int i = 0; i < 10000; i++) { setDone();realA.incrementAndGet(); }}private void setDone() {flag = true;   // 纯赋值操作符合预期 // flag = !flag ; // 这样做不符合预期 }
}
class Test{public static void main(String[] args) throws InterruptedException { UseVolatile1 r =  new UseVolatile1();Thread thread1 = new Thread(r); Thread thread2 = new Thread(r);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println(r.flag);System.out.println(r.realA.get()); }
}

在这里插入图片描述
触发器
按照volatile的可见性和禁止重排序以及happens-before规则,volatile可以作为刷新之前变量的触发器。我们可以将某个变量设置为volatile修饰,其他线程一旦发现该变量修改的值后,触发获取到的该变量之前的操作都将是最新的且可见。

public class VisibilityHP {int a = 1;int b = 2;int c = 3;volatile boolean flag = false; private void write() {a = 3;b = 4 ;c = a;flag = true; }private void read() {// flag被volatile修饰,充当了触发器,一旦值为true,此处立即对变量之前的操作可见。 while(flag){System.out.println("a=" + a + ";b=" + b +",c="+c ); }}public static void main(String[] args) { while (true) {VisibilityHP test = new VisibilityHP(); new Thread(new Runnable() {@Overridepublic void run() { try {Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace();}test.write(); }}).start();new Thread(new Runnable() { @Overridepublic void run() { try {Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace();}test.read(); }}).start();}}}

volatile可以作为刷新之前变量的触发器。我们可以将某个变量设置为volatile修饰,其他线程一旦发现该变量修改的 值后,触发获取到的该变量之前的操作都将是最新的且可见。

volatile与synchronized

在这里插入图片描述

volatile总结

在这里插入图片描述

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

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

相关文章

十款外贸软件盘点,专注企业订单业务管理

在当今全球化的市场环境中&#xff0c;外贸企业的发展面临着诸多挑战与机遇。如何高效管理企业业务&#xff0c;提升运营效率&#xff0c;成为外贸企业在激烈竞争中脱颖而出的关键。外贸业务管理ERP软件作为一种强大的工具&#xff0c;能够整合企业资源、优化管理流程、实现数据…

yaml文件编写

Kubernetes 支持YAML和JSON格式管理资源 JSON 格式:主要用于 api 接口之间消息的传递 YAML 格式;用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化容易读懂 一&#xff0c;yaml语法格式 1.1 基本语法规则 使用空格进行缩进&#xff08;不使用制表符&#xff0…

Node.js 全栈开发进阶篇

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;node.js篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来node.js篇专栏内容:node.js- 全栈开发进阶篇 前言 大家好&#xff0c;我是青山。在上一篇文章中&#xff0c;…

Vue(JavaScript)读取csv表格并求某一列之和(大浮点数处理: decimal.js)

文章目录 想要读这个表格&#xff0c;并且求第二列所有价格的和方法一&#xff1a;通过添加文件输入元素上传csv完整&#xff08;正确&#xff09;代码之前的错误部分因为价格是小数&#xff0c;所以下面的代码出错。如果把parseFloat改成parseInt&#xff0c;那么求和没有意义…

C语言初阶必会的练习题(3)之位操作符(^ 、、>>等)的应用

C语言初阶必会的练习题&#xff08;3&#xff09; 放在最前面的1、不允许创建临时变量&#xff0c;交换两个整数的内容1.1、分析&#xff1a;见代码注释&#xff08;a&#xff09;方法 1&#xff08;b&#xff09;方法 2 1.2、结果展示方法 1 的 结果&#xff1a;方法 2 的 结果…

基于SSM框架的乡村农户对口扶贫系统

基于SSM框架的乡村农户对口扶贫系统。 设计步骤&#xff1a; 项目架构创建&#xff1a;首先创建项目的基本架构&#xff0c;包括com.zc.xxx路径下的文件和resources资源文件夹。 SSM架构&#xff1a;使用Spring、SpringMVC、MyBatis作为后端架构&#xff0c;采用POJO—Dao—…

微服务透传日志traceId

问题 在微服务架构中&#xff0c;一次业务执行完可能需要跨多个服务&#xff0c;这个时候&#xff0c;我们想看到业务完整的日志信息&#xff0c;就要从各个服务中获取&#xff0c;即便是使用了ELK把日志收集到一起&#xff0c;但如果不做处理&#xff0c;也是无法完整把一次业…

十五:java web(7)-- Spring Boot

目录 1. Spring Boot 简介 1.1 简介 1.2 Spring Boot 的特点 1.3 Spring Boot 和 Spring 的关系 2. Spring Boot 快速入门 2.1 创建第一个 Spring Boot 项目 3. Spring Boot 配置管理 3.1 application.properties 和 application.yml 配置 这两种都可以 好像现在更推荐…

关于打开网页非常慢的解决方法

方法一&#xff1a;刷新dns缓存 ipconfig /flushdns方法二&#xff1a;许多网站&#xff0c;太落后&#xff0c;不支持ipv6&#xff0c;所以关闭ipv6即可

JDK1.5 java代码打包jar HmacSha256

文章目录 demo地址背景实现编写代码编译class文件打包 JAR 文件执行生成的 JAR 文件辅助验证方式 常见问题和解决方法常规生成jar方案maven插件idea工具 demo地址 https://github.com/xiangge-zx/HmacSha256 背景 最近接到一个需求,做一个可以用来HmacSha256加密的小工具&am…

Go八股(Ⅳ)***slice,string,defer***

***slice&#xff0c;string&#xff0c;defer*** 1.slice和arry的区别 arry&#xff1a; Go语言中arry即为数据的一种集合&#xff0c;需要在声明时指定容量和初值&#xff0c;且一旦声明就长度固定&#xff0c;访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

Win系统通过命令行查看笔记本电池损耗/寿命/健康

在 Windows 10/11 系统中&#xff0c;可以通过指令查看笔记本电池的寿命情况&#xff0c;方法如下&#xff1a; 0&#xff0c;打开cmd/终端 键盘快捷键&#xff1a;Win R&#xff0c;然后输入cmd&#xff0c;点击【确定】 1&#xff0c;执行命令 在命令行中输入下面指令并按…

103 - Lecture 1

Introduction to Database 一、Introduction to Database Systems 1. 数据的定义 What is Data? EX: data could be a docx file storing your project status report; data could be a spreadsheet containing information • 数据只有在设计的场景中才有意义。&#xff…

【大数据学习 | kafka高级部分】kafka中的选举机制

controller的选举 首先第一个选举就是借助于zookeeper的controller的选举 第一个就是controller的选举&#xff0c;这个选举是借助于zookeeper的独享锁实现的&#xff0c;先启动的broker会在zookeeper的/contoller节点上面增加一个broker信息&#xff0c;谁创建成功了谁就是主…

关于几种卷积

1*1卷积 分组卷积&深度可分离卷积 空洞卷积、膨胀卷积 转置卷积 https://zhuanlan.zhihu.com/p/80041030 https://yinguobing.com/separable-convolution/#fn2 11的卷积可以理解为对通道进行加权&#xff0c;对于一个通道来说&#xff0c;每个像素点加权是一样的&am…

OCR、语音识别与信息抽取:免费开源的AI平台在医疗领域的创新应用

一、系统概述 在医疗行业中&#xff0c;大量数据来自手写病历、医学影像报告、患者对话记录等非结构化数据源。这些数据常常存在信息碎片化和管理困难的问题&#xff0c;给医务人员的工作带来了不便。思通数科AI多模态能力平台正是为了解决这一行业痛点而生&#xff0c;产品集…

Git进阶(十八):git rebase详解

文章目录 一、前言二、rebase 图解三、应用示例四、重建提交历史五、rebase VS merge六、拓展阅读 一、前言 rebase 使用方法 git rebase [基节点] git rebase [基节点] [待变基节点]rebase后面的参数可以是两个&#xff0c;也可以是一个&#xff0c;当rebase为一个参数的时…

【React】条件渲染——逻辑与运算符

条件渲染——逻辑与&&运算符 你会遇到的另一个常见的快捷表达式是 JavaScript 逻辑与&#xff08;&&&#xff09;运算符。在 React 组件里&#xff0c;通常用在当条件成立时&#xff0c;你想渲染一些 JSX&#xff0c;或者不做任何渲染。 function Item({ nam…

《深度学习》——深度学习基础知识(全连接神经网络)

文章目录 1.神经网络简介2.什么是神经网络3.神经元是如何工作的3.1激活函数3.2参数的初始化3.2.1随机初始化3.2.2标准初始化3.2.3Xavier初始化&#xff08;tf.keras中默认使用的&#xff09;3.2.4He初始化 4.神经网络的搭建4.1通过Sequential构建神经网络4.2通过Functional API…

Bsin-kb-agent:企业级AI知识库

企业级AI知识库 Bsin-kb-agent 是基于BsinPaaS开源框架和大语言模型构建的企业知识库应用&#xff0c;借鉴langchain的框架思想&#xff0c;引入langchian4j组件&#xff0c;微前端微服务的架构设计&#xff0c;可快速助您构建和体验端到端的AI知识库应用。 应用场景 企业微…