• Volatile的作用


    众所周知,volatile关键字可以让线程的修改立刻通知其他的线程,从而达到数据一致的作用。那么它具体涉及到哪些内容呢?

    关于缓存

    计算机最大的存储空间就是磁盘(硬盘),但是访问的速度也是最慢的,价格最便宜;再就是内存,容量更小,造价更高,但是速度也更快。不过跟cpu的计算速度比起来,那就太慢了。可以想像,如果cpu每次计算都要从内存读取数据,那大部分的时间估计都浪费在这上面了。所以就引入了缓存的概:

    缓存的结构大概时这样的,从1级到3级速度越来越慢,最后通过总线与内存连接。如果时多核多cpu,那么结构大概是这样的:

    多线程造成的缓存不一致

    由于现在大部分的机器都有多个cpu,这就导致如果时运行多线程的任务,就可能运行在不同的cpu上。试想一下:

    int a = 0;
    int b = a;
    b += 1;
    

    如果开启两个线程执行,我们想要的结果是3,但是最后的结果只是2。这是因为在做加法运算的时候,cpu会先把a的值读入cpu的缓存,然后更新缓存,在更新内存。很有可能两个线程分散在两个cpu,每个都是对自己缓存内的数据进行读写,这样就造成了结果不一致的现象。

    volatile的作用

    volatile的作用就是当一个线程更新某个volatile声明的变量时,会通知其他的cpu使缓存失效,从而其他cpu想要做更新操作时,需要从内存重新读取数据。具体的通知方式,一种是通过某种协议,比如MESI;再就是对总线加锁,控制变量的读取。具体硬件上怎么个流程,我就搞不清楚了...

    并发

    这里还需要强调的时,并发编程涉及的三个特性:原子性、可见性、有序性。就好像分布式里面的cap一样,需要熟知。先来通俗的描述下:

    原子性

    即要么全做,要么全部做。比如从a银行转钱到b银行。

    在编程中,除了long或者double外的变量更新就是原子操作。long和double除外,是因为它们在32位的操作系统上,会被分成两部分进行更新,此时就不是原子的。

    再比如最常见的i++也不是原子的,它相当于先读取i进行+1操作更新三个步骤进行。

    可见性

    多个线程访问同一个变量时,这个变量被修改后,能被其他的线程看到。

    有序性

    比如

    int a = 10; 
    int r = 2;   
    a = a + 3;  
    r = a*a;    
    

    这段代码有可能进行指令的重排,从而导致结果跟预期的不一致。指令的重排需要按照happens-before原则,比如:

    1. 程序次序原则,一个线程内,按照书写的顺序执行
    2. 锁定原则,lock前后执行
    3. volatile原则,volatile变量前后执行
    4. 传递原则,如果a需要调用b,那么a就会在b的前面

    ...

    等等...

    volatile的特性

    volatile只能保证变量的可见性、有序性,但是不能保证原子性。因此可以用它来做double-check,但是不能来做i++的操作。如果想要实现i++的可靠性,必须依赖于synchronized、lock或者atomicXXX来实现。

    参考

    1. 海子的《Java并发编程:volatile关键字解析》:http://www.cnblogs.com/dolphin0520/p/3920373.html
    2. liuxiaopeng的《Java 并发编程:volatile的使用及其原理》:https://www.cnblogs.com/paddix/p/5428507.html
    3. double check http://blog.csdn.net/dl88250/article/details/5439024
    4. cpu缓存知识:http://blog.jobbole.com/36263/
  • 相关阅读:
    case when then用法
    查询后n条记录
    自定义函数
    字符函数
    数字运算符和函数
    时间日期函数
    mysql加密函数
    比较运算符和函数
    文件夹中的文件以目录的形式呈现
    错误提示:通过 Web 服务器的身份验证的用户无权打开文件系统上的文件
  • 原文地址:https://www.cnblogs.com/xing901022/p/7840684.html
Copyright © 2020-2023  润新知