zoukankan      html  css  js  c++  java
  • volatile关键字

    volatile关键字


    synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volatile则是轻量级的synchronized。如果一个变量使用volatile,它比使用synchronized的成本更加低,因为它不会引起线程上下文的切换和调度。

    计算机在运行程序时,每条指令都是在CPU中执行的,在执行过程中势必会涉及到数据的读写。我们知道程序运行的数据是存储在主存中,这时就会有一个问题,读写主存中的数据没有CPU中执行指令的速度快,如果任何的交互都需要与主存打交道则会大大影响效率,所以就有了CPU高速缓存。CPU高速缓存为某个CPU独有,只与在该CPU运行的线程有关。

    有了CPU高速缓存虽然解决了效率问题,但是它会带来一个新的问题:数据一致性。在程序运行中,会将运行所需要的数据复制一份到CPU高速缓存中,在进行运算时CPU不再与主存打交道,而是直接从高速缓存中读写数据,只有当运行结束后才会将数据刷新到主存中。

    volatile在wiki中的解释

    volatile用于一个作用域时,Java保证如下:

    1. (适用于Java所有版本)读和写一个volatile变量有全局的排序。也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。(但是并不保证经常读写volatile作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。
    2. (适用于Java5及其之后的版本)volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁。

    使用volatile会比使用锁更快,但是在一些情况下它不能工作。volatile使用范围在Java5中得到了扩展,特别是双重检查锁定现在能够正确工作。

    一个经典例子

    代码块1:
    public class RecordExample2 {
        int a = 0;
        boolean flag = false;    //0
    
        /**
         * A线程执行
         */
        public void writer(){
            a = 1;                  // 1
            flag = true;            // 2
        }
    
        /**
         * B线程执行
         */
        public void read(){
            if(flag){                  // 3
               int i = a + a;          // 4
            }
        }
    
    }
    

    通过前面的学习我们知道,由于存在重排序,在代码块1中,这两个线程执行后在4中得到的结果是不确定的。这时候就可以使用volatile关键字,在0处改为:

    volatile boolean flag = false;
    

    volatile 关键字会使 flag 变量对后面的程序可见, 从而保证了程序的正确性。

    总结

    1. 保证可见性、不保证原子性
    2. 禁止指令重排序

    通常来说,使用volatile必须具备以下2个条件:

    1.对变量的写操作不依赖于当前值

    2.该变量没有包含在具有其他变量的不变式中


    参考资料:

    有关原子性,可见性和有序性,此文值得一看(此文最后有volatile的经典用法——单例模型中的双重检查。)

    死磕java系列

  • 相关阅读:
    「疫期集训day11」沙漠
    「树形DP」洛谷P2607 [ZJOI2008]骑士
    「疫期集训day10」玫瑰
    「疫期集训day9」七月
    核心容器(概念)
    初识Spring
    IOC(控制反转思想)原型理论推导
    图片在上,文字在下并且等间距的三个菜单按钮
    编写登陆接口
    001使用gltf创建3d模型
  • 原文地址:https://www.cnblogs.com/fruitknife/p/9703073.html
Copyright © 2011-2022 走看看