学过计算机组成原理的一定知道,为了解决内存速度跟不上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. PHP SOAP 教程

    一.SoapServer 服务器 1.__construct 作用:创建 SoapServer 对象 用法:__construct ( mixed wsdl [, array options] ) 参 ...

  2. [Debug]SpaceVim中neomake报错 Error while trying to load a compilation database

    回家装上archlinux,突发奇想装个SpaceVim写题 安装配置一路可以说是没有太大问题 最后在写题时出现如下问题 Error while trying to load a compilatio ...

  3. luogu P1495 曹冲养猪(中国剩余定理)

    题意 题解 翻到了一个金句 就跟这句话说得一样,就是个裸题. 所以看模板呗. #include<iostream> #include<cstring> #include< ...

  4. CF1019C Sergey's problem (图上构造)

    题目大意:给你一个有向连通图,让你找出一个点集,保证点集内的点之间没有直接连边,且集合中存在一点,到一个 非点集中的点的距离小于等于2 思路很清奇 首先编号从小到大遍历每个点,如果这个点没有被访问过, ...

  5. makefile--回顾基础篇

    前阵子让写makefile,纠结了下,基本忘记差不多了. 1.gcc的编译选项 -c 只是编译不链接,生成目标文件“.o” -S 只是编译不汇编,生成汇编代码 -E 只进行预编译,不做其他处理 -g ...

  6. tree 核心命令参数

    常用参数: -a  显示所有文件 包括隐藏文件 -d 只显示目录 -f 显示每个文件的全路径 -i 不显示树枝 常与-f一起搭配 -L 显示的层数 -F 区分哪个文件是目录 [root@ftp:/va ...

  7. Android触碰事件

    OnTouchListener使用 public class ViewActivity extends Activity implements View.OnTouchListener { @Over ...

  8. 由动态库文件dll生成lib库文件

    本文基于OpenBlas的编译和安装.来说明怎样从一个dll文件生成lib库文件. 參考OpenBlas的说明"Howto generate import library for MingW ...

  9. node06---npm、silly-datetime、路径问题

    我们刚才学习了,模块就是一些功能的封装,所以一些成熟的.经常使用的功能,都有人封装成为了模块.并且放到了社区中,供人免费下载. 这个伟大的社区,叫做npm. 也是一个工具名字 node package ...

  10. What is the difference between SET and SELECT when assigning values to variables, in T-SQL?

    http://vyaskn.tripod.com/differences_between_set_and_select.htm https://stackoverflow.com/questions/ ...