学过计算机组成原理的一定知道,为了解决内存速度跟不上CPU速度这个问题,在CPU的设计中加入了缓存机制,缓存的速度介于CPU和主存之间。在进行运算的时候,CPU将需要的数据映射一份在缓存中,然后直接操作位于缓存中的数据,操作完毕后再将缓存中的数据写回到主存。这在单线程环境中是没有任何问题的。但是在多线程环境中就大不同了。 
假设现在有这样的一个场景:有两个线程thread1和thread2,他们都在操作位于主存上的一个数据int a=2(具体操作为读取a的值并执行一个自增操作)。逻辑上正确的结果:应当是最后a=4。但可能有这样的情况,thread1将a=2从主存映射到自己的工作内存上,自增后变成a=3,在将a=3从工作内存写回到主存之前,thread2也将a=2从从主存映射到自己的工作内存上,也自增后变成a=3。然后两个线程先后将a=3写回到主存上。显然,a=3不是我们想看到的。看,这就是一个常见的缓存一致性问题。两个线程对a的操作结果互不可见,thread1不知道thread2对a进行了自增,thread2也不知道thread1对a进行了自增。在多线程编程中就是会出现这样一致性的问题。(在JMM中,可以知道,内存分为主内存和工作内存,每个线程有自己 的工作内存,他们共享主内存)。 
因此我们要办法让线程对共享变量的操作结果互相可见,java语言中的volatile关键字就干了一件这样的事。使用volatile修饰的共享变量,当有线程修改了他的值的时候,他会立即强制将修改的值写回到主存,并通知其他使用该共享变量的线程:他们的缓存区中关于此变量的值已经失效。请重新从主存中读取。 
仔细阅读volatile干的事,一共有3点影响: 
1 将修改的值强制刷新到主存 
2 通知其他相关线程变量已经失效 
3 其它线程再使用变量的时候就会重新从主存读取
 
这就解决了JAVA并发编程中的可见性问题。 
可见性:当多个线程访问同一个共享变量的时候,一个线程对该共享变量的修改能够实时的被访问该共享变量的其他线程知晓。 
继续说上面的那个例子,如果变量a被使用了volatile修饰,那么在thread1中,当a变为3的时候,就会强制刷新到主存。如果这个时候,thread2已经将a=2从从主存映射到缓存上,那么在对a进行自增操作以前,会重新到主存中读取a=3,然后自增到a=4,然后写回到主存。上面的过程很完美,但这样是否保证了a最终的结果一定是4呢?未必。 
继续说上面的那个例子,如果变量a被使用了volatile修饰,那么在thread1中,当a变为3的时候,就会强制刷新到主存。如果这个时候,thread2已经将a=2从从主存映射到缓存上并且已经做完了自增操作,此时a=3,那么最终主存中a的值为3。 
所以,如果我们想让a的最终值是4,仅仅保证可见性是不够的,还得保证原子性。也就是对于变量a的自增操作加锁,保证任意一个时刻只有一个线程对a进行自增操作。可以说volatile是一种“轻量级的锁”,它能保证锁的可见性,但不能保证锁的原子性。 
volatile变量的一种典型用法,就是用于那些状态的标记,比如:

  1. volatile boolean flag=false;
  2. while(!flag){
  3. doSomething();
  4. }

在其他线程中,可能会修改flag的值为true,代表退出循环。如果不使用volatile修饰flag,可能在flag被回收之后,主线程还没收到其值改变的消息。这是volatile的一种典型应用。当然我们也可以使用volatile类型的不可变对象来缓存最新的内容。对于上一篇博客http://yizhenn.iteye.com/blog/2286623讲的那个例子:根据一个请求的1-9的阿拉伯数字返回对应的大写汉字的servlet类。

  1. public class OneValueCache{
  2. private final Integer lastNum;
  3. private final String lastStr;
  4. public OneValueCache(Integer num,String str){
  5. this.lastNum=new Integer(num);
  6. this.lastStr=new String(str);
  7. }
  8. public String getLastStr(int num){
  9. if(num==null || !num.equles(lastStr))
  10. return null;
  11. else
  12. return new String(lastStr);
  13. }
  14. }
    1. @ThreadSafe
    2. public class MyServlet implements Servlet
    3. {
    4. private volatile OneValueCache cache=new OneValueCache(null,null);
    5. public void service(ServletRequest req,ServletResponse res){
    6. int i=getNum(req);
    7. String str=cache.getLastStr(i);
    8. if(str==null){
    9. str=getHanziByNum(i)
    10. cache=new OneValueCache(i,str);
    11. }
    12. responseHanzi(res,str);
    13. }
    14. }

JAVA并发--volatile的更多相关文章

  1. Java并发-volatile的原理及用法

    Java并发-volatile的原理及用法 volatile属性:可见性.保证有序性.不保证原子性.一.volatile可见性 在Java的内存中所有的变量都存在主内存中,每个线程有单独CPU缓存内存 ...

  2. java 并发——volatile

    java 并发--volatile 介绍 维基百科: volatile 是一个类型修饰符(type specifier).volatile 的作用是确保本条指令不会因编译器的优化而省略,且要求每次直接 ...

  3. Java并发——volatile的原理

    111 Java并发——volatile的原理

  4. Java 并发 —— volatile 关键字

    volatile 修饰变量等于向编译器传达如下两层含义: 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 禁止进行指令重排序. volat ...

  5. Java并发--volatile关键字

    一.volatile的实现原理 synchronized是阻塞式同步,在线程竞争激烈的情况下会升级为重量级锁,而volatile就可以说是JVM提供的最轻量级的同步机制.JMM告诉我们,各个线程会将共 ...

  6. Java并发--volatile详情

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

  7. Java并发——volatile关键字

    什么是内存可见性? 这里就要提一下JMM(Java内存模型).当线程在运行的时候,并不是直接直接修改电脑主内存中的变量的值.线程间通讯也不是直接把一个线程的变量的值传给另一个线程,让其刷新变量.下面是 ...

  8. Java并发——volatile关键字的使用

    volatile关键字的使用volatile关键字原理适合使用volatile关键字的情况当且仅当满足以下所有条件时,才==应该==使用volatile关键字:volatile关键字的作用volati ...

  9. Java并发——volatile

    CPU的内存模型如下:

随机推荐

  1. NodeJS学习笔记 (7)网络服务-http-client(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: ClientRequest概览 当你调用 http.request(options ...

  2. centos7 命令

    firewall-cmd --zone=public --add-port=80/tcp --permanent 开启端口 systemctl stop firewalld.service  关闭服务 ...

  3. 紫书 习题 11-4 UVa 1660 (网络流拆点法)

    这道题改了两天-- 因为这道题和节点有关, 所以就用拆点法解决节点的容量问题. 节点拆成两个点, 连一条弧容量为1, 表示只能经过一次. 然后图中的弧容量无限. 然后求最小割, 即最大流, 即为答案. ...

  4. Qt5.7新特性

    简述 Qt5.7发布了,新特性如下. 简述 新特性 C11 Support Required from the compiler New Features within existing module ...

  5. Android面试精华

    SIM卡的EF文件有什么作用? SIM卡里的全部文件按树来组织: 主文件MF(Master File)--主文件仅仅有文件头,里面存放着整个SIM卡的控制和管理信息 专用文件DF(Dedicated ...

  6. android 反编译和代码解读

    二 错误代码还原规则 if…else 语句: 反编译代码 if (paramBoolean) paramTextView.setTextColor(-16727809); while (true) { ...

  7. poj_2299Ultra-QuickSort,树状数组离散化

    求逆序数 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm&g ...

  8. thinkphp命名空间

    thinkphp命名空间 总结 1.只对函数,类,及const定义的常量有效,对define定义的常量无效 2.如果函数不是为了使用,那有什么意义呢 3.ThinkPHP将命名空间转化为了路径,比如n ...

  9. [GDKOI2010] 圈地计划(网络流)

    题2链接:https://www.luogu.org/problemnew/show/P1935 Description 最近房地产商GDOI(Group of Dumbbells Or Idiots ...

  10. 解析UML用例图中include与extend的区别

    UML用例图有很多值得学习的地方,这里向大家简单介绍一下UML用例图中include与extend的区别,希望本文的介绍对你有所帮助. 本文和大家重点讨论一下UML用例图中include与extend ...