zoukankan      html  css  js  c++  java
  • java 并发之volatile

    volatile有以下作用:

    阻止重排序,保证可见性,对于单次读写保证原子性(不包括i++这种复合操作)。

    下面分别予以解释。

    众所周知(也许有些人不知道),编译器以及CPU会在不影响串行结果的情况下对代码进行重排序,以便加快执行速度。(比如超标量流水线技术)

    这在并发执行中会造成一些问题。以单例模式的DCL写法举例:

    public class Singleton{

      private Singleton(){};

      public static volatile Singleton singleton;

      public static Singleton getSingleton(){

        if(singleton == null){

          synchronized(singleton){

            if(singleton == null){

              singleton = new Singleton();

            }

          }

        }

        return singleton;

      }

    }

    双重检查加锁好理解,一是为了保证单一实例,而是为了减少并发开销。为什么又用了volatile了呢?

    因为橙色标注的部分实际上有三步:

    分配内存,初始化对象, 引用赋值。

    但由于重排序的存在,有可能会先分配内存,然后赋值,最后才初始化。并发下就有可能将未初始化的对象暴露出来。这也是为什么要用volatile的原因。

    jdk1.5后, volatile语义中有了happen-before关系约束。保证了对volatile对象的写操作,一定在对其后续的读操作前完成。

    也就杜绝了这种重排序造成错误的情况。

    另外可见性是指由于线程有自己单独的内存栈,而全局变量一般在堆内存里面,所以单个线程的更改可能不会及时同步到堆,造成改动不可见问题。

    加了volatile后,每次改动都会强制刷新堆内存,及其他线程栈内存,保证了数据的最新性,也就是可见性。

    原子性是对long,double这种64位变量来说,由于虚拟机是32位的,所以对其读写要分两次,用volatile约束,保证了读写的原子性。

    要注意,volatile保证的原子性仅限于此。对于复合操作,如i = i + 1; i++;这种,并不保证。

    所以复合操作的原子性还是要用synchronized来保证。

    synchronized保证了最强的原子性,可见性。

  • 相关阅读:
    设计模式-创建型-原型模式
    设计模式-创建型-抽象工厂模式
    设计模式-创建型-工厂模式
    设计模式-创建型-单例模式
    css3技巧属性之text-overflow
    bootstrap如何自定义5等分
    LeetCode-64-最小路径和
    LeetCode-62-不同路径
    LeetCode-5-最长回文子串
    LeetCode-98-验证二叉搜索树
  • 原文地址:https://www.cnblogs.com/zqiguoshang/p/6942130.html
Copyright © 2011-2022 走看看