Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字。

volatile,从字面上说是易变的、不稳定的,事实上,也确实如此,这个关键字的作用就是告诉编译器,只要是被此关键字修饰的变量都是易变的、不稳定的。那为什么是易变的呢?因为volatile所修饰的变量是直接存在于主内存中的,线程对变量的操作也是直接反映在主内存中,所以说其是易变的。

什么是主内存?为什么是在主内存中?先看看java的内存模型(JMM)中内存与线程的关系。

图片来自《深入理解Java虚拟机》

JMM中的内存分为主内存和工作内存,其中主内存是所有线程共享的,而工作内存是每个线程独立分配的,各个线程的工作内存之间相互独立、互不可见。在线程启动的时候,虚拟机为每个内存分配了一块工作内存,不仅包含了线程内部定义的局部变量,也包含了线程所需要的共享变量的副本,当然这是为了提高执行效率,读副本的比直接读主内存更快。

那么对于volatile修饰的变量(共享变量)来说,在工作内存发生了变化后,必须要马上写到主内存中,而线程读取到是volatile修饰的变量时,必须去主内存中去获取最新的值,而不是读工作内存中主内存的副本,这就有效的保证了线程之间变量的可见性。

volatile特性一:内存可见性,即线程A对volatile变量的修改,其他线程获取的volatile变量都是最新的。

举个栗子:

volatile boolean flag;
...
while(!flag){
doSomeThing();
}

检查标记判断退出循环

volatile的例子很难重现,因为只有在对变量读取频率很高的情况下,虚拟机才不会及时写回到主内存,而当频率没有达到虚拟机认为的高频率时,普通变量和volatile是同样的处理逻辑。

volatile特性二:可以禁止指令重排序

至于重排序是啥?我们通过个简单的例子了解下。

public class SimpleHappenBefore {/** 这是一个验证结果的变量 */private static int a=0;/** 这是一个标志位 */private static boolean flag=false;public static void main(String[] args) throws InterruptedException {//由于多线程情况下未必会试出重排序的结论,所以多试一些次for(int i=0;i<1000;i++){ThreadA threadA=new ThreadA();ThreadB threadB=new ThreadB();threadA.start();threadB.start();//这里等待线程结束后,重置共享变量,以使验证结果的工作变得简单些.threadA.join();threadB.join();a=0;flag=false;}}static class ThreadA extends Thread{public void run(){a=1;flag=true;}}static class ThreadB extends Thread{public void run(){if(flag){a=a*1;}if(a==0){System.out.println("ha,a==0");}}}
}

这里有两个共享变量a和flag,初始值分别为0和false。在ThreadA中先给a=1,然后flag=true。 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为真,永远不会打印。
但实际情况是,在试验100次的情况下会出现0次或几次的打印结果,而试验1000次结果更明显,有十几次打印。

以上这种现象就是由于指令重排序造成的。
那么什么是指令重排序?–为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则将程序编写顺序打乱。

如果变量没有volatile修饰,程序执行的顺序可能会进行重排序。

浅谈volatile关键字相关推荐

  1. 浅谈Dynamic 关键字系列之三(下):ExpandoObject,DynamicObject,DynamicMetaObject

    接上文:浅谈Dynamic关键字系列之三(上) 为什么TryXXX方法没有被调用?? 将DynamicProduct 中的name修饰符改为private: private string name; ...

  2. 浅谈Volatile与多线程

    http://renyan.spaces.eepw.com.cn/articles/article/item/86826 随着多核的日益普及,越来越多的程序将通过多线程并行化的方式来提升性能.然而,编 ...

  3. 浅谈volatile

    前言 提到volatile关键字的原理,我这里不想赘述,其实我也讲不好,因为这不是一篇简短的文章和我这个水平就能讲的清楚的,我建议你如果想了解一下volatile的原理,可以去看一下<Java并 ...

  4. php 浅谈static 关键字

    static  关键字定义静态方法和属性, 也可用于定义静态变量以及后期静态绑定 定义静态变量时 <?php function Test() { $aa= 0; echo $aa; $aa++; ...

  5. 浅谈Dynamic 关键字系列之二:调用属性,方法,字段

    新建类Product: class Product {public string name;public int Id { get; set; }public void ShowProduct(){C ...

  6. setnx是原子操作吗_谈谈Volatile关键字?为什么不能保证原子性?用什么可以替代?为什么?...

    大家好,欢迎关注我的公众号码猿bug,需要资料的话可以加我微信好友. 再谈volatile关键字之前,首先必须聊聊JMM内存模型! JMM主要的特性:可见性.原子性,顺序性 Java 虚拟机规范试图定 ...

  7. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  8. php字面量,浅谈js之字面量、对象字面量的访问、关键字in的用法

    一:字面量含义 字面量表示如何表达这个值,一般除去表达式,给变量赋值时,等号右边都可以认为是字面量. 字面量分为字符串字面量(string literal ).数组字面量(array literal) ...

  9. java中virtual关键字_浅谈virtual、abstract方法和静态方法、静态变量理解

    说点对这几个容易混淆的词的理解: 1.c++中的virtual方法的 virtual关键字主要是防止继承中重复继承父类的同一个方法而设置的标识. 2.virtual与abstract关键字的不同之处在 ...

最新文章

  1. 衡阳a货翡翠,南平a货翡翠
  2. AI前沿:数据智能产品与技术漫谈
  3. 分支优化:neg+sbb算术运算代替逻辑跳
  4. 青龙羊毛——小虎饿了(偷的)
  5. mysql 四 表操作
  6. 新建angular-cli项目
  7. y sinx matlab,有一个函数 f(x,y)=x^2+sinxy+2y ,用matlab写一个程序 输入自变量的值,输出函数的值....
  8. DiscuzNT改造-远程图片自动采集-DNT2.5(自动采集、源码下载)
  9. Android 安装腾讯X5内核
  10. 天正网络版服务器填写位置,教你如何在天正里面输入坐标定位
  11. 万年历显示c语言百度文库,C语言万年历
  12. Javascript实现用户注册验证
  13. 微信小程序-图片放大与缩小
  14. Unity MasterServer主服务器
  15. flash学习者不要错过-视频教程打包下载
  16. PAT——A1008Elevator(模拟)
  17. 绝对值的计算(自用)
  18. 什么是域名劫持?遇到域名劫持要怎么处理
  19. PPC修改注册表大全 (注册表必看)(转载)
  20. 简单说 如何做一个chrome 去广告插件

热门文章

  1. Error: Current license file does not support the EP1C6Q240C8/EP3C10E144C8 device
  2. 博客开园了~~~~~~
  3. python input函数详解_对Python3中的input函数详解
  4. 电子测量与仪器第四版pdf_电子技术经典资料汇总:模电篇800M
  5. 三种前端手机开发(uni-app、vant、mui)
  6. 双链表插入、删除操作单步解析(十四)
  7. Linux简单调用so库及Makefile用法
  8. Android5.1 Audio计算音量流程
  9. 结合源码探讨Android系统的启动流程
  10. GB28181-2016过检通过