首页 > 其他分享 >volatile关键字的作用以及底层原理

volatile关键字的作用以及底层原理

时间:2024-09-27 21:49:08浏览次数:12  
标签:变量 Barrier 关键字 线程 内存 volatile 共享 底层

volatile关键字的作用以及底层原理


前言

在java并发编程中,volatile关键字可以保证数据的可见性和防止JVM指令的重排序,我们接下来深入讲讲这些问题是什么以及volatile关键字是如何解决这些问题的


java的内存模型结构

如下图所示,在当今的java内存结构中,多线程访问的共享变量存储在主内存中,每一个线程拥有它们自己的本地内存,主内存是它们共享的内存空间。当线程读取共享变量之后会将共享变量的副本放入它们的本地内存中,当线程下次读取时会直接从本地内存中读取,当修改共享变量时,会先修改本地内存中的共享变量的副本,而后再将共享变量的最新值刷新到主内存中。

在这里插入图片描述

数据的不一致和指令的重排序

数据的不一致问题:从上图的java内存模型中,可能会发生这样一个问题:
如果一个线程要修改共享变量,需要两个步骤:
A1:首先要修改本地内存的共享变量,
A2:然后将共享变量的最新值同步到主内存中,
此时另一个线程想要读取这个共享变量,直接从该线程的本地内存读取,无法读取共享变量的最新值,造成了数据的不一致。
指令的重排序:编译器和处理器为了优化程序的性能,可能会对JVM生成的指令进行重排序,对于单线程来说,为了遵守as-if-serial语义,编译器和处理器不会对具有数据依赖关系的操作进行重排序,但是对于多线程来说,重排序可能会破坏多线程程序的语义。

内存屏障

volatile使用内存屏障来保证数据的可见性和防止指令重排序。内存屏障其实是一种指令JVM,在java编译器在生成JVM指令时插入特定的内存屏障指令。
内存屏障粗分为:
Load Barrier:在指令之前插入屏障,使工作内存或cpu高速缓存中的缓存数据失效,返回主内存中读取最新的数据
Store Barrier:在指令之后插入屏障,强制将缓存区中的数据更新到主内存中
细分为:
LoadLoad Barrier:实例Load1;LoadLoad;Load2,确保Load1的读操作先于Load2以及后续操作执行
StoreStore Barrier:实例Store1;StoreStore;Store2,在Store2以及后续操作执行之前,确保Store1的写操作修改的数据已经刷新到主内存中
LoadStore Barrier:实例Load1;LoadStore;Store2,确保Load1操作先于Store2以及后续操作执行
StoreLoad Barrier:实例Store1;StoreLoad;Load2,在Load2以及后续操作执行之前,确保Store1的写操作修改的数据已经刷新到主内存中

volatile读写插入的内存屏障

在volatile读之后加入了LoadLoad Barrier和LoadStore Barrier,防止volatile读操作和后续的volatile读和volatile写错做重排序
在volatile写前加入了StoreStore Barrier:防止volatile写操作和之前的volatile写操作和其他操作重排序
在volatile写之后加入了StoreLoad Barrier:确保在后续的读操作之前,volatile写操作已经把修改的数据同步到主内存中

volatile不保证数据的原子性

比如i++操作,volatile不保证原子性,因为i++是一个复合操作,包含三部:
获取旧值、更新值、返回新值,如果第二个线程在第一个线程获取旧值和读取新值之间读取i的值,就造成了线程安全问题

volatile的内存语义

volatile写:当写一个volatile变量时,JMM会立即将当前线程本地内存修改的共享变量刷新到主内存中
volatile读:当读一个volatile变量时,JMM会将当前线程的本地内存设置为无效,直接从主内存中读取共享变量

标签:变量,Barrier,关键字,线程,内存,volatile,共享,底层
From: https://blog.csdn.net/m0_71338251/article/details/142556074

相关文章

  • Spring Ioc底层原理代码详细解释
    文章目录概要根据需求编写XML文件,配置需要创建的bean编写程序读取XML文件,获取bean相关信息,类,属性,id前提知识点Dom4j根据第二步获取到的信息,结合反射机制动态创建对象,同时完成属性赋值将创建好的bean存入到Map集合,设置key-value映射提供方法从Map中通过id获取到对象的valu......
  • STL之vector篇(下)(手撕底层代码,从零实现vector的常用指令,深度剖析并优化其核心代码)
    文章目录1.基本结构与初始化1.1空构造函数的实现与测试1.2带大小和默认值的构造函数1.3使用迭代器范围初始化的构造函数(建议先看完后面的reserve和push_back)1.4拷贝构造函数1.5赋值操作符的实现(深拷贝)1.6析构函数1.7`begin`与`end`迭代器2.容量管理2.1`re......
  • 三大硬核方式揭秘:Java如何与底层硬件和工业设备轻松通信!
    大家好,我是V哥,程序员聊天真是三句不到离不开技术啊,这不前两天跟一个哥们吃饭,他是我好多年前的学员了,一直保持着联系,现在都李总了,在做工业互联网相关的项目,真是只要Java学得好,能干一辈子,卷死的是那些半吊子。感谢李总给我分享了工业互联网项目的事情,收获很多,今天的内容来聊一聊......
  • 一篇文章讲清楚synchronized关键字的作用及原理
    概述在应用Sychronized关键字时需要把握如下注意点:一把锁只能同时被一个线程获取,没有获得锁的线程只能等待;每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁synchronized修饰......
  • 底层设计:轮询系统 - 边缘情况
    目录案例1-处理更新的版本控制情况2-pollid作为uuid而不是主键情况3-选项为空或无效案例4-重复选项案例5-问题长度限制案例6-投票过期请先参考以下文章:底层设计:投票系统:基本底层设计:轮询系统-使用node.js和sql边缘情况处理案例1要管理投票......
  • 三大硬核方式揭秘:Java如何与底层硬件和工业设备轻松通信!
    大家好,我是V哥,程序员聊天真是三句不到离不开技术啊,这不前两天跟一个哥们吃饭,他是我好多年前的学员了,一直保持着联系,现在都李总了,在做工业互联网相关的项目,真是只要Java学得好,能干一辈子,卷死的是那些半吊子。感谢李总给我分享了工业互联网项目的事情,收获很多,今天的内容来聊一......
  • JAVA基础:lock锁底层机制
    目录lock锁底层机制乐观锁lock锁底层机制lock锁底层使用的是CAS+AQS,在lock底层有一个计数器,记录锁被获取的状态,起初为0,当被抢占的时候变为1当我们调用lock.lock()方法,就是将状态从0改为1的过程。当我们调用lock,unlock()方法时,就是将状态从1改为0的过程当我们调用......
  • Elasticsearch基本概念及底层 【总结】
    随着业务的增长,数据与日俱增,这时为用户带来丰富的、便捷的搜索功能就迫在眉睫了。传统的数据库在处理文本搜索、模糊查询、海量数据统计分析的时候总会力不从心,所以在处理这些复杂的搜索需求时,我们更倾向于使用Elasticsearch搜索引擎。Elasticsearch是一个分布式、RESTf......
  • 关于 ReentrantLock 中锁 lock() 和解锁 unlock() 的底层原理浅析
    关于ReentrantLock中锁lock()和解锁unlock()的底层原理浅析一、描述如下代码,当我们在使用ReentrantLock进行加锁和解锁时,底层到底是如何帮助我们进行控制的啦?staticLocklock=newReentrantLock();publicstaticvoidmain(String[]args){/......
  • java中abstract关键字的使用案例
    创建动物父类跟狗猫子类abstractclassAnimal1{//抽象类Stringname;intage;Stringvariety;publicAnimal1(){}publicAnimal1(Stringname,intage,Stringvariety){//有参构造this.age=age;this.name=name;......