【JAVA并发学习四】volatile分析

来源:互联网 发布:sketch up mac vary 编辑:程序博客网 时间:2024/06/05 17:30

关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易完全正确、完整的理解。了解volatile变量的语义对后面了解多线程操作的其他特性很有意义。

一 理解volatile

volatile用来定义变量用的,当一个变量被定义为volatile后,他将具备两种特性:

  • 保证可见性,但不保证原子性:保证此变量对所有线程的可见性,这里可见性指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的,但是不保证原子性例如自增操作(num++)
  • 禁止指令重排序优化

这两个特性保证了volatile实现最轻量级的同步机制,下面会仔细讲解,这两个特性是如何实现的

二 保证可见性

JMM通过两个操作实现volatile变量的可见性:

  • 对volatile变量进行操作后,会立即将变量的变动写入主内存中
  • 本地内存中访问volatile变量之前,都必须要先从主内存中更新该变量的值

三 不保证原子性

当volatile变量参与的运算是非原子性时,导致volatile变量的运算在并发下一样是不安全的。例如对于volatile int num变量,进行num++操作,由于自增操作不具有原子性,所以导致对于volatile变量的自增操作时线程不安全的

四 禁止重排序

volatile通过内存屏障实现对重排序的禁止,我们通过一个双锁检测实现单例模式的例子来讲解:

    public class Singleton {        private volatile static Singleton instance;        public static Singleton getInstance() {            if (instance == null) {                synchronized (Singleton.class) {                    if (instance == null) {                        instance = new Singleton();                    }                }            }            return instance;        }        public static void main(String[] args) {            Singleton singleton = Singleton.getInstance();        }    }

这段代码中instanse是volatile变量,我们先假设下如果instance不是volatile变量时会发生什么。结论是在多线程并发情况下会发生错误,具体原因就在于instance = new Singleton();这行代码可以分为三行

    memory = allocate();           //1:分配对象所需的堆空间    ctorInstance(memory);          //2:初始化对象    instance = memory;             //3:让instance指向对象的内存地址

JVM在真正执行上面代码时,可能会进行重排序,如果将2、3执行顺序进行调换。有个A、B两个线程,A线程执行完1、3后CPU的执行权被收回从而进入Runnable状态,B线程开始运行,此时instace变量为null,B创建了一个Single对象。B执行完A继续执行2,这是A也创建了另一个Single对象,导致出错。

可以发现出错的原因在于2、3的执行顺序被重排序了。要防止这个错误可以通过禁止2、3重排序,volatile就可以做到。当instance是volatile变量时,会在3的位置设置内存屏障,保证在3之前的代码在3执行之前都已经执行完毕,3之后的代码在3执行后才能执行。

通过查看编译后的文件,可以发现第三句编译成了 0x01a3de24: lock addl $0x0,(%esp),关键就在于这个lock前缀,他的作用在于是的本CPU的Cache写入内存,并使得别的CPU中的Cache无效,使得volatile变量instance的修改被其他CPU立即可见。当把Cache中修个的变量同步至内存时,意味着所有之前的操作都已经执行完成,这样便形成了“指令重排序无法超越内存屏障”的效果

五 总结

volatile可以说是Java提供的最轻量级的同步机制,他可以保证变量的可见性,但不保证原子性,同时能够禁止重排序