用法解释

一旦一个共享变量(类的成员变量、类的静态成员变量)被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. form表单那点事儿(上) 基础篇

    form表单那点事儿(上) 基础篇 做为html中最为常见,应用最广泛的标签之一,form常伴随前端左右.了解更深,用的更顺. 目录: 表单属性 表单元素 常识 模拟外观 表单属性 这个表单展示了fo ...

  2. [转载]Using ngOptions In AngularJS

    http://odetocode.com/blogs/scott/archive/2013/06/19/using-ngoptions-in-angularjs.aspx?utm_source=tui ...

  3. javascript正则——贪婪匹配

    熟悉正则的朋友都知道,正则的匹配有“贪婪”和“非贪婪”之分. “贪婪”匹配是尽可能多的匹配: 对于字符串‘aaaa’, /a+/匹配整个字符串,而非贪婪匹配/a+?/匹配的是整个字符串的第一个‘a’, ...

  4. cocos基础教程(13)使用Physicals代替Box2D和chipmunk

    1.   概述 游戏中模拟真实的世界是个比较麻烦的事情,通常这种事情都是交给物理引擎来做.首屈一指的是Box2D了,它几乎能模拟所有的物理效果.而chipmunk则是个更轻量的引擎,能够满足简单的物理 ...

  5. django程序报错CSRF verification failed. Request aborted.

    django程序的html页面中form的method='post'的时候报错 Forbidden (403) CSRF verification failed. Request aborted.He ...

  6. [BZOJ1477]青蛙的约会

    [BZOJ1477]青蛙的约会 试题描述 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘 ...

  7. Linux 日志文件utmp、wtmp、lastlog、messages

            1.有关当前登录用户的信息记录在文件utmp中:==who命令 2.登录进入和退出纪录在文件wtmp中:==w命令 3.最后一次登录文件可以用lastlog命令察看: 4.messag ...

  8. vim实用技巧

    <1> 删除空格: :% s/ //gi    #正则为一个空格,替换为空,全局匹配. <2> 删除空行: :g /^\n*$/ d  #g为global  正则为:行开始+换 ...

  9. 【Python】python代码如何调试?

    Python 程序如何高效地调试? 现在我在debug python程序就只是简单在有可能错误的地方print出来看一下,不知道python有没像c++的一些IDE一样有单步调试这类的工具?或者说各位 ...

  10. Django中如何查找模板

    参考:http://my.oschina.net/zuoan001/blog/188782 Django的setting中有关找模板的配置有如下两个: TEMPLATE_LOADERS TEMPLAT ...