美文网首页
多线程Volatile关键字

多线程Volatile关键字

作者: 西5d | 来源:发表于2017-12-09 22:20 被阅读15次

    volatile变量是一种轻量级,简单,有效的同步机制。volatile变量具备两种特性:

    1. 保证变量对所有线程的可见性,即当一个线程修改了这个值,新值对其他线程来说是立即可得到的。而普通变量的值在线程传递中都需要通过主内存来完成。A写入普通变量的值,向主内存回写,B线程再从主内存获取。⚠️注意:volatile看起来像是在直接读取主内存,实际线程对变量的所有操作(读取,赋值)都是在各自的工作内存中进行,相互之间无法共享工作内存。
      Java线程,工作内存,主内存
      注意:
      volatile变量只能保证可见性,在以下两种情况的运算场景中,仍然需要通过枷锁来保证原子性。
    • 运算结果依赖变量的当前值,或者不能确保只有单一线程来修改变量的值。
    • 变量需要与其他的状态变量共同参与不变约束。
    1. 禁止指令重排序优化。除了增加高速缓存,CPU内部为了充分利用运算单元,处理器可能会对输入的代码进行乱序执行,会在计算后重组结果,保证与顺序执行的结果是一致的,但不保证程序中各个语句计算的先后顺序与输入代码中完全一致。Java虚拟机即时编译器中也有指令重排序优化。细节是volatile修饰的变量,赋值后多了一个lock寄存器的空操作,即内存屏障(重排序时不能把后面的指令重排序到屏障之前的位置),使得CPU的cache写入了内存,同时引起其他
      CPU或者内核刷掉自己的Cache,相当于一次store和write操作,保证了对volatile变量的修改对其他的CPU立即可见。

    volatile的优势与缺点:
    读操作的性能消耗和普通变量几无差别,但是写操作可能会较慢写,因为要在本地代码中插入许多内存屏障指令来保证处理器内核不发生乱序执行,但相对来说,volatile在大多数场景下的总开销比锁低。volatile适合经常读取,少写入,可见性优先,非依赖自身结果的并发场景下使用,例如状态标记值,配置信息等变量。

    补充内容

    并发编程的三个特性

    1.原子性

    原子性理解为对变量或者对象的操作不会有中间状态,结果有确定性,要么成功,要么失败。java内存模型保证原子性变量操作的包括read,load,assign,use,store,write。大致认为所欲基本数据类型的访问是原子性的(long和double在64位和32位下有半个变量的情况仅做了解,因为操作实现是原子性的)。以及lock和unlock操作保证原子性,还有synchronized同步块,对应字节码monitorenter和monitorexit隐式操作。

    2. 可见性

    可见性指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。java内存模型是通过在变量修改后将新的值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,⚠️无论普通变量还是volatile变量都是如此,volatile的特殊规则保证了新值能立即同步到主内存(内存屏障,刷Cache),以及每次使用前立即从主内存刷新。
    补充:final和synchronized保证可见性。同步块通过在对一个变量进行unlock之前,必需现报变量同步回主内存中。而final关键字是通过:被final修饰的字段,一旦在构造器中初始化完成,并且构造器没有将this引用传递出去(this引用逃逸,其他线程有可能通过这个this获取初始化了一半的对象),在其他线程中就可以看到final字段的值——这条规则来实现可见性。

    3. 有序性

    Java程序的有序性概括:在本线程内,所有的操作都是有序的——线程内的串行;而线程间观察,所有操作都是无序的——指令重排序和工作内存和主内存同步。⚠️volatile提供禁止指令重排序来保证有序。synchronized可以使得一个变量在同一时刻只允许一条线程对其进行lock操作保证有序,意思是同一个锁的两个同步块只能串行的进入。
    ⚠️虽然synchronized同步块同时满足三种特性,但因为锁的粒度较大,某些情况下有很大的性能影响

    相关文章

      网友评论

          本文标题:多线程Volatile关键字

          本文链接:https://www.haomeiwen.com/subject/vjupixtx.html