在很多面试中会经常被问到“volatile”关键字,而volatile关键字是java并发编程中一个非常重要的关键字,这个问题也是比较底层的,下面让我来分享一下我关于这两个问题的一些理解~希望可以帮助到大家~

首先,我们先简单了解一下什么是volatile?volatile 是 Java 中的关键字,是一个变量修饰符,被用来修饰会被不同线程访问和修改的变量,是java虚拟机提供的轻量级的同步机制。

一、volatile关键字的作用

volatile 关键字有两个作用

  • 可以保证在多线程环境下共享变量的可见性
  • 可以屏蔽在多线程环境下CPU的指令重排

可见性:当某一个线程对共享变量的修改,其他线程可以立刻看到修改之后的值

这个可见性的问题,我认为本质上是由以下两个方面造成的:

首先是CPU的高速缓存,如下图所示在CPU里面设计了三级缓存,它用来去解耦CPU的运算效率和内存IO的效率问题,但是这样的设计,又带来了缓存的一致性问题,而在多线程并行的情况下,缓存一致性又会导致可见性的问题,所以,对于加了volatile关键字修饰的共享变量来说,JVM虚拟机会自动增加一个Lock的一个汇编指令,那么这个指令会根据CPU的型号自动添加给总线锁或者是缓存锁

JAVA线程中volatile关键字的作用 多线程volatile关键字的作用_多线程

下面简单介绍一下上述两种锁:

总线锁是锁定了CPU的前端总线,从而导致在同一时刻,只有一个线程去内存通讯,这样,就避免了多线程并发造成的可见性,缓存锁则是对总线锁的优化,因为总线锁的使用,导致了CPU的使用效率大幅下降,所以缓存锁只针对于CPU的三级缓存中的目标数据进行加锁,而缓存锁是使用MESI缓存一致性来实现的。

屏蔽指令重排:屏蔽CPU指令重排序。在多线程环境下,CPU指令的编写顺序和执行顺序不一致,从而导致可见性问题

为了提升 CPU 的利用率,CPU引入了StoreBuffer机制,而这一种优化机制会导致 CPU 的乱序执行

为了避免这样的问题,CPU 提供了内存屏障指令,上层应用可以在合适的地方插入内存屏障来避免CPU 指令重排序问题。而volatile就是通过设置内存屏障,来禁止指令重排的。

二、volatile的实现原理分析

1、volatile会在变量写操作前后加入内存屏障

JAVA线程中volatile关键字的作用 多线程volatile关键字的作用_内存屏障_02

 如上图所示,volatile会在变量写操作前后加入两个内存屏障,来保证前面写的指令和后面读的指令是有序的

2、volatile在变量的读操作后插入两个指令

JAVA线程中volatile关键字的作用 多线程volatile关键字的作用_内存屏障_03

如上图所示,volatile在变量的读操作后插入两个指令,禁止后面的读指令和写指令重排序。volatile可以看作是轻量级的synchronized,虽然说volatile不能保证原子性,但是如果在多线程环境下的操作本身就是原子操作的话,那么使用volatile会优于synchronized。