整理volatile相关知识点

前言:volatile关键字在面试中经常被问到,从volatile关键字可以引申出许多知识点,因此有必要对此进行总结。本文根据《深入理解Java虚拟机——JVM高级特性与最佳实践》中的相关章节,整理得来。


相关名词

TPS(Transactions Per Second):每秒事务处理数,衡量一个服务性能好坏的评判标准。

JMM(Java Memory Model):Java内存模型。

1.硬件上解决数据一致性

由于CPU有高速缓存机制,所以在程序运行时,会将需运算的数据从主存中复制一份到高速缓存中,在CPU进行计算时,直接在缓存中进行数据的读取和写入,在运算完成后,再将缓存中的数据刷新到主存中。这种模式在单线程中是没有任何问题的,但在多线程中会出现数据不一致的问题,如i++问题。对变量的操作涉及三个步骤:读取值、操作值和重新写入新值,这就是在多线程出现数据不一致的原因。

解决方式:

①在总线上加LOCK锁的方式

在运算时,直接在总线上加锁,可以避免缓存不一致问题,但是CPU利用率极低。

②缓存一致协议,参考:https://www.cnblogs.com/yjf512/p/5166415.html

2.JMM内存模型

JMM:用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。

JMM主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量在主存和从内存中如何取出的规则。此处的变量包括实例字段静态字段和构成数组对象的元素,不包括局部变量和方法参数(线程私有)。

JMM中规定变量都存在于主存中,线程都有自己的工作内存,线程对变量(是对主存中变量的副本拷贝)的操作必须在工作内存中,而不能直接对主存中数据进行操作,并且每个线程不能访问其他线程的工作内存,线程间变量值的传递需要通过主内存来完成。也就是说线程工作内存中进行数据的操作,然后再更新主存中的值。因此在多线程中可能出现i++问题。

对于主内存与工作内存中变量之间的交互,JMM中定义了8种操作来完成,这8种操作具有原子性。

①lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程的独占状态。

②unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

③read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load操作使用。

④load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。

⑤use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将执行该操作。

⑥assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行该操作。

⑦store(存储):作用于工作内存的变量,它把工作内存中的一个变量的值传递到主内存中,以便随后的write操作使用。

⑧write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量值放入主内存的变量中(更新操作)。

注:从主内存到工作内存,需顺序执行read和load,从工作内存到主内存,需顺序执行store和wirte操作,JMM只要求这些操作是顺序执行的,并不保证连续执行。

要保证并发的正确执行,就需要保证原子性、可见性和有序性。只要有一个条件未满足,就可能导致程序运行结果不正确。

在JMM中是如何保证并发执行的正确性的,有如下三点:

①JMM只保证基本的读取和赋值是原子性操作,如i=10;如果需要更大范围的原子性,则需要synchronized和Lock的帮助了。

②可见性:由volatile关键字提供,当然通过synchronized和Lock也可以实现。

③有序性:JMM中具备一些先天的“有序性”(happens-before原则),通过volatile也可保证一定的有序性,当然通过synchronized和Lock也可保证有序性

3.volatile关键字

被volatile修饰的变量,具有两种特性:

①保证变量对所有线程是可见的,当一条线程修改了变量的值时,新值对于其他线程来说是可以立即得知的。

对变量进行修改涉及两个操作:a.修改线程工作内存中的值;b.将修改后的值更新到主内存。

当当前线程对volatile变量的值修改时,会导致其他线程中的变量缓存无效,所以其他线程再次读取volatile变量的值时,会从主存中获取。

②禁止指令重排序优化

指令重排序优化是指处理器为了提高程序运行的效率,可能会对输入的代码进行优化,它不保证代码的执行顺序与书写顺序一致,但保证程序的最终执行结果和书写顺序的结果一致。指令

volatile能在一定程度上保证有序性,为什么说一定程度上看下面解释。

禁止指令重排序优化有两层意思:

a.当程序执行到volatile变量的读操作或者写操作时,在其前面的操作全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。

从该点可以看出volatile只能在一定程度上保证有序性:A->volatile->B,整体上按照这种顺序执行,但是在A操作里还是可能存在指令重排序,只要其操作对后续操作无影响。

b.在进行指令优化时,不能将volatile变量的语句放在volatile位置之前执行,也不能把volatile变量语句放到volatile语句之后执行。可能这句话有点难以理解,通过下面伪代码简单说明。

1 //a、b为非volatile变量
2 //flag为volatile变量
3
4 a = 1;         //语句1
5 b = 2;         //语句2
6 flag = true;   //语句3
7 a = 3;         //语句4
8 b = 4;         //语句5

因为flag为volatile变量,所以执行指令重排序优化时,不能将语句3放在在语句1、2的前面;也不能将语句3放在语句4、5的后面执行。并且执行到语句3时,语句1、2已经执行完毕,并且其结果对语句3、4、5是可见的。注意语句1、2和语句4、5的顺序不做任何保证(这里也说明了volatile只能在一定程度上保证有序性)。

volatile的两种特性,主要由内存屏障提供(volatile原理和实现机制)。

加入volatile关键字时,会多出一个lock前缀指令,该lock前缀指令实际上相当于一个内存屏障,内存屏障会提供3个功能:

1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

2)它会强制将对缓存的修改操作立即写入主存。

3)如果是写操作,它会导致其他CPU中对应的缓存行无效

volatile不能保证原子性,其原因可通过i++操作来分析:

i初始值为0,并且被volatile修饰,当线程1读取i后,还没对i进行自增操作时被阻塞,此时线程2进来,虽然volatile保证了线程2是从主存中读取的数据,但是由于线程1并未对i值进行修改,所以线程2不会发现修改后的i值,这就是volatile不保证原子性的根源。

4.happens-before原则

happens-before(先行发生原则)主要是用来保证JMM的先天“有序性”,如果两个操作的执行顺序不能遵循happens-before原则,则虚拟机就可以对其进行重排序优化。

①程序次序规则:一个线程内,按照代码顺序,前面的操作对后面的操作先行发生。(主要保证单线程执行结果的正确性)

②锁定规则:一个unLock操作后,才能对同一个锁Lock操作,也就说对于同一锁,需要先unLock,其次才进行Lock。

③volatile变量规则:对一个变量的写操作对后面对这个变量的读操作先行发生。

④传递规则:如果操作A对操作B先行发生,而操作B对操作C也先行发生,则可以得出操作A对操作C先行发生。(传递性)

⑤线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作。

⑥线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

⑦线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行。

⑧对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始。

对于happens-before原则,前四条非常重要。

总结:

volatile关键字引申出来的知识点非常多,笔者认为主要有以下几点:

①硬件层面上解决数据一致性的方式(两种)。

②JMM的相关知识,主要理解主内存(主存)和工作内存,以及read和load、store和write这两对操作。

③在JMM中如何保证并发程序执行的正确性的(原子性、可见性(volatile)、有序性(happens-before原则))。

④volatile的底层原理:内存屏障。



by Shawn Chen 2018.6.4日,上午。

原文地址:https://www.cnblogs.com/morewindows0/p/9129342.html

时间: 2024-10-13 21:22:14

整理volatile相关知识点的相关文章

数据库相关知识点(秋招整理)

数据库 1.   数据库事务的 4 个特性是:原子性.一致性.持续性.隔离性 1)   原子性:事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行. 2)   一致性:事务前后,数据库的状态都满足所有的完整性约束. 3)   隔离性:并发执行的事务是隔离的,一个不影响一个.如果有两个事务,运行在相同的时间内,执行相同的功能,同一时间仅有一个请求用于同一数据.设置数据库的隔离级别,可以达到不同的隔离效果. 4)   持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保

个人学习 php 语言的相关知识点目录

本文用于记录个人学习php语言的相关知识点整理,给各位打算了解php知识的童鞋提供参考: 除了最基本的html.css.js知识,以下知识是一个php开发必备的知识点,也是web开发中常用的知识点. 以下是进阶阶段需要掌握的相关知识: xml编程 smarty模板技术 常用php框架技术,thinkphp(容易理解)等. ajax jquery linux环境下的开发 版本控制:svn和git 缓存技术:redis.memcached.页面技术等 Mysql等服务器软件:数据库设计.sql语句优

iOS-Runtime知识点整理,ios-runtime知识点

iOS-Runtime知识点整理,ios-runtime知识点 本文目录 1.Runtime简介 2.Runtime相关的头文件 3.技术点和应用场景 3_1.获取属性\成员变量列表 3_2.交换方法实现 3_3.类\对象的关联对象,假属性 3_4.动态添加方法,拦截未实现的方法 3_5.动态创建一个类 4.面试题 -1.Runtime简介 回到顶部 1.Runtime简介 因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时.也就是说只有编译器是不够的,还需要一个运

垂直搜索的相关知识点总结

垂直搜索引擎大体上需要以下技术 1.Spider 2.网页结构化信息抽取技术或元数据采集技术 3.分词.索引 4.其他信息处理技术 垂直搜索引擎的技术评估应从以下几点来判断 1.全面性 2.更新性 3.准确性 4.功能性 垂直搜索的进入门槛很低,但是竞争的门槛很高.没有专注的精神和精湛的技术是不行的.行业门户网站具备行业优势但他们又是没有技术优势的,绝对不要想像着招几个人就可以搞定垂直搜索的全部技术,作为一个需要持续改进可运营的产品而不是一个项目来说对技术的把握控制程度又是垂直搜索成功的重要因素

博客6:磁盘以及创建文件系统的相关知识点

1.linux磁盘的相关知识点  (1)I/O ports:I/O设备地址,实质上是一个缓冲器  (2)块设备:Block,存取单位是"块",例如:磁盘  (3)字符设备:char,存取单位是"字符",例如:键盘  (4)设备文件:关联至一个设备驱动程序,进而能够与之对应硬件设备进行通信(仅有元数据,而无数据)  (5)设备号码:              主设备号:major number,表示设备类型              次设备号:minor number

图像处理程序框架—MFC相关知识点

CDC:Windows使用与设备无关的图形设备环境(DC :Device Context) 进行显示 . MFC基础类库定义了设备环境对象类----CDC类.CDC与CGdiObject的关系 说道CDC类就不能不提一下GdiObject---图形对象类. 在Windows应用程序中,设备环境与图形对象共同工作,协同完成绘图显示工作.就像画家绘画一样,设备环境好比是画家的画布,图形对象好比是画家的画笔.用画笔在画布上绘画,不同的画笔将画出不同的画来.选择合适的图形对象和绘图对象,才能按照要求完成

libgdx相关知识点

Gdx.graphics.setContinuousRendering(false); 设置图像为非连续自动渲染. libgdx相关知识点,布布扣,bubuko.com

Android开发涉及有点概念&相关知识点(待写)

前言,承接之前的 IOS开发涉及有点概念&相关知识点,这次归纳的是Android开发相关,好废话不说了.. 先声明下,Android开发涉及概念比IOS杂很多,可能有很多都题不到的.. 首先由于Android是基于Linux,而Linux又是用c山寨Unix的,但是为什么Android不用开发App,其实我也不懂,我想可能是java比较简单. 同样的,IOS为啥没用java,这个我也想可能是因为IOS是封闭的吧..晕,又废话了..好,开始吧,不过这次很多要查资料了,很多名词.单词不会啊! 首先还

<c:url>标签相关知识点

一. <c:url>标签的作用主要用来产生字符串类型的url,而不是产生一个超链接,注意url(统一资源定位符)与超链接两个含义的区别. <c:url value="http://www.baidu.com" />在JSP页面中的输出形式如下: 可以看出来这在页面中就是个字符串,不是一个能点击的超链接. --------------------------------------------------------------------------------