这道题想考察什么?
- 是否了解volatile的使用?
- 是否了解volatile的特性与实现原理?
考察的知识点
- volatile的使用
- JMM
- 指令重排
- volatile的实现原理
考生应该如何回答
1、首先,我们简单介绍一下volatile关键字。不过,在解释volatile前,我们需要了解并发编程的3个基本概念。
- 原子性:一个操作或者多个操作,要么就全部执行完,要么就都不执行。
- 可见性:多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
- 有序性:程序执行的顺序按照代码的先后顺序执行。
volatile是Java提供的一个轻量级同步机制,作为并发编程里一个重要组成部分,它用来修饰变量。通过volatile修 饰的变量可以保证可见性与有序性。在双重检查加锁方式实现的单例中,就有使用,看下面这个再也不能更熟悉的单例代码。
public class ClientService extends BaseService { //使用volatile修饰单例变量 private volatile static ClientService sInstance; //获取单例,双重检查加锁方式 public static ClientService getInstance() { if (sInstance == null) { synchronized (ClientService.class) { if (sInstance == null) { //注意!!!这里看似一行代码,其实分三步走: //第一步:为ClientService对象分配内存 //第二步:实例化对象 //第三步:将sInstance引用指向实例 sInstance = new ClientService(); } } } return sInstance; } //构造方法私有 private ClientService() { }}
2、接下来,我们就可以针对volatile的特性来一一解释,关于可见性问题需要从Java内存模型这块入手,简称JMM。
在JMM中,为了提高效率,抽象出一个主内存与工作内存的概念。线程之间的共享变量存储在主内存中,另外每个线程又都配备了一个私有的工作内存,工作内存中使用到的变量需要到主内存去拷贝,线程对变量的读取、赋值操作都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成。
那volatile如何保证可见性的呢?
当我们的变量被volatile修饰后,在生成汇编代码指令时会在volatile修饰的共享变量进行写操作的时候添加一个Lock前缀。
Lock前缀表示当一个线程修改该共享变量后,它会将新值立即刷新到主内存中,同时导致该变量在所有线程的本地内存失效,这样其他线程再读取共享变量时,会直接从主内存中读取,达成缓存一致性。这里与synchronize或者Lock等锁机制保证可见性的做法还是有差别的。锁机制的做法是保证同一时刻只有一个线程获取锁并执行同步代码,释放锁时将对变量的修改刷新到主存当中。
3、说完可见性,再来看看有序性。有序性问题需要归咎于指令重排。在Java内存模型中,如果某些指令之间不存在数据依赖,为了提高效率,是允许编译器和CPU对指令进行重排序,当然重排序不会影响单线程的运行结果,但是对多线程会有影响。我们可以简单的这样理解,打个比方:
//第一种情况,a与b没有依赖关系int a = 2;int b = 3;//第二种情况,a与b没有依赖关系,但是c依赖a和bint a = 2;int b = 3; int c = a + b;
对于第一种情况,a与b之间没有数据依赖关系,机器指令就会进行重新排序,可能先会执行b的赋值,再去执行a的赋值,这样单线程肯定没问题,但是如果有另一个线程同时在使用变量b,就有可能引发并发问题。再看第二种情况,因为c依赖a和b的值,所以在这种情况下,机器指令就不会把c的赋值排到在a和b的前面。
那volatile如何保证有序性的呢?
为了避免指令重排引起的并发问题,volatile就大派用场了。volatile的实现具体是这样做的,在Java编译器生成指令时,对于volatile关键字修饰的变量,会在指令序列中插入特定的内存屏障。
内存屏障又是什么呢?其实它并不是什么高深的东西,说到底也是一个指令而已,例如StoreStore、StoreLoad、LoadLoad、LoadStore等,它特殊的地方,通俗来讲就是告诉编译器和CPU,不管什么指令都不能和我的内存屏障指令重排序,大致就是这个意思,关于内存屏障相关内容,这里就不过多赘述了。
总而言之,记住一点,使用volatile关键字修饰的变量会禁止指令重排,从而保证了有序性。
总结
最后,我们还是总结一下,这道题的答案其实很简单,一两句话便可以概括,volatile就是保证了可见性与有序性。当然,volatile的内部原理还是需要深究一下的,指不定面试官要跟你往深处唠唠呢!