用法解释

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  2)禁止进行指令重排序(他上面的比他先执行,他下面的比他后执行,但并不保证上面和下面的代码块也是有序执行)

保证可见性:

  volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

不保证原子性:

  譬如inc++,自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致下面这种情况出现:

  假如某个时刻变量inc的值为10,

  线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;

  然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。

  然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。

  那么两个线程分别进行了一次自增操作后,inc只增加了1。

  解释到这里,前面保证的是一个变量在修改volatile变量时,会让缓存行无效,然后其他线程去读就会读到新的值。但是要注意,线程1对变量进行读取操作之后被阻塞了,并没有对inc值进行修改。然后虽然volatile能保证线程2对变量inc的值读取是从内存中读取的,但是线程1没有进行修改,所以线程2根本就不会看到修改的值。根源就在这里,自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的。

一定程度上保证有序性:

  volatile关键字禁止指令重排序有两层意思:

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

  2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

原理解释

JVM角度:

  Java使用一个主内存来保存变量当前值,而每个线程则有其独立的工作内存。

  线程访问变量的时候会将变量的值拷贝到自己的工作内存中,这样,当线程对自己工作内存中的变量进行操作之后,就造成了工作内存中的变量拷贝的值与主内存中的变量值不同。

  Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

  这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

  而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,会使在工作内存中的私有拷贝失效,从而再去主内存取值。

汇编角度:

  “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

  lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

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

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

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

使用条件:

  通常来说,使用volatile必须具备以下2个条件:

    1)对变量的写操作不依赖于当前值

    2)该变量没有包含在具有其他变量的不变式中

    实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。

    事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。

下面列举几个Java中使用volatile的几个场景。

1.状态标记量

volatile  boolean  flag =  false ;
 //!flag是原子性操作,修改flag后即可见
while (!flag){
     doSomething();
}
 
public  void  setFlag() {
     flag =  true ;

}

volatile  boolean  inited =  false ;
//线程1:
context = loadContext();  
//保证运行inited =  true ;之前已被初始化,用到了一定程度的有序性
inited =  true ;            
 
//线程2:
while (!inited ){
sleep()
}
doSomethingwithconfig(context);

 

volatile的理解的更多相关文章

  1. Java线程工作内存与主内存变量交换过程及volatile关键字理解

    Java线程工作内存与主内存变量交换过程及volatile关键字理解 1. Java内存模型规定在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内存拷贝主内存变量副本来进行.此处的所谓内存模 ...

  2. volatile变量理解 via《Java并发编程实战》

    第3章:对象的共享 volatile关键字的理解 volatile变量,用来确保将变量的更行操作通知到其他线程.当变量申明为volatile类型后,编译器与运行时都会注意带这个变量时共享的,因此不会将 ...

  3. 对volatile的理解--从JMM以及单例模式剖析

    请谈谈你对volatile的理解 1.volitale是Java虚拟机提供的一种轻量级的同步机制 三大特性1.1保证可见性 1.2不保证原子性 1.3禁止指令重排 首先保证可见性 1.1 可见性 概念 ...

  4. 对于volatile的理解

    哎.要学的东西太多,时间太少.一周的工作下来要总结的东西太多,还处理不完,越积越多.大周末的好想出去玩啊.... 得嘞,废话止于此. 无聊时候乱看网页发现了volatile的一篇文章,以前曾经对vol ...

  5. volatile的理解和使用

    package thread; /** * Created by Administrator on 2017/1/15. */ public class Counter { public volati ...

  6. 关于java多线程关键字volatile的理解

    volatile关键字的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值. 使用volition关键字增加了实例变量在多个线程间的可见性.但volition有个致命的缺点就是不 ...

  7. (转)volatile 的理解

    对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成: 1) (unsigned char *)0x20,0x20只是个值,前面加(unsigned cha ...

  8. Volatile关键字理解

    Volatile定义 为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量.Java语言提供了volatile,在某些情况下比锁更加方便.如果一个字段被声明成volatile, ...

  9. java中关于volatile的理解疑问?

    作者:xyzZ链接:https://www.zhihu.com/question/49656589/answer/117826278来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

随机推荐

  1. Grunt之项目脚手架

    在网上搜了下,grunt这方面的教程挺少的,来去都是一些被频繁转载的文章.唉,人艰不拆啊. 首先我们在全局环境中安装grunt-init. npm install -g grunt-init 来看看官 ...

  2. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  3. javaScript模块化规范ADM与CMD

    模块化:模块化是指在解决某一个复杂问题时,依照一种分类的思维把问题进行系统性的分解处理,可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在. 模块化系统所必须 ...

  4. linux下Nginx服务器安装教程

    序:Nginx服务器安装总结而已,不是教程. 安装的过程中出现了一些问题,原因我的云主机是纯净版,所以很多依赖包都没有.其中安装过程中就发现perl库缺少和openssl库缺少,因此我手动安装的这两款 ...

  5. 背景透明的 Dialog

    一:控制Dialog 的背景方法:1.定义一个无背景主题主题 <!--去掉背景Dialog--> <style name="NobackDialog" paren ...

  6. Unable to execute dex: Multiple dex files define

    这是一个编译错误,在ADT的编译器和SDK的工具有差异或是版本不一致时常会出现的一个问题,解决的方案如下: 第一步: updated eclipse (Help->Check for updat ...

  7. Apache Common DbUtils

    前段时间使用了Apache Common DbUtils这个工具,在此留个印,以备不时查看.大家都知道现在市面上的数据库访问层的框架很多,当然很多都是包含了OR-Mapping工作步骤的 例如大家常用 ...

  8. Python 正则表达式_re模块_使用compile加速

    使用compile加速 compile( rule [,flag] ) 将正则规则编译成一个Pattern对象,以供接下来使用. 第一个参数是规则式,第二个参数是规则选项. 返回一个Pattern对象 ...

  9. 解决android:theme="@android:style/Theme.NoDisplay" 加入这句话后程序不能运行

    原因: 原来用的是ActionBarActivity,继承自 ActionBarActivity的类必须指定固定的集中Theme风格,而这些 Theme 风格是需要导入V7中的 appcompat L ...

  10. mysql 多表连接

    现有表R,S如下: 笛卡尔积 select * from R,S; 结果: 注:不需要任何条件.结果为两张表函数相乘(3x3=9). 自连接 select e.empno,e.ename,m.empn ...