前言
Volatile的关键字从Java5出现后就存在了,很多人做了很长时间的Java开发都未必用的到这个关键字,其实它还是很有用的一个关键字。
正文
简单概述
volatile是Java提供的一种轻量级的同步机制,同synchronized相比,volatile更轻量级,在访问volatile变量时不会执行加锁操作,因此也就不会使执行的线程阻塞。
变量不加volatile
问题1 可见性问题
CPU每次执行指令的时候会对内存进行读写操作,尽管内存的读写速度比硬盘的读写速度快得多,但是指令的处理速度更快,从内存进行读写速度相对于指令执行的速度就慢了,所以CPU就会将线程用到的变量以及一些复杂的操作复制到缓存(寄存器)中处理,然后再将处理的结果写入内存。但是对于多核CPU,线程可能运行在不同的CPU缓存中,这样的话就无法保证线程的可见性(指线程之间的可见性,一个线程修改的变量后,另一个线程取到变量也是修改后的变量)。
//初始化
int i =10;
//每个线程都对i++
i++;
对于i来说,线程A从内存读取i后存入缓存,它取到的值是10,线程B也将i从内存中读入缓存,取到值也是10。线程A对i++,i就变成了11,但是线程B中的i还是10,那么i变量对于线程是不可见的了。
问题2 有序性问题
int i = 0;//A步骤
int j = 1;//B步骤
i++;//C步骤
j++;//D步骤
CPU为了保证性能或者在多个线程存在的情况下,对于指令的处理顺序会有变化(指令重排序:CPU允许将多条指令不按程序规定的顺序分别给各个相应的处理器处理),比如它可能先执行了B步骤,然后在执行A步骤,也可能先执行了D步骤,然后再执行C步骤。
变量加volatile
线程可见性
//初始化
volatile int i =10;
对于一个变量赋予了volatile关键字后那么变量对所有的线程具有可见性,当一个线程修改了这个变量的值,编译器就不会将这个变量复制到缓存中,就直接在内存中操作,这样从内存中取到的volatile标识的变量就是最新的值,也就对其它线程可见了。
线程有序性
volatile可以禁止指令重排序,volatile标识的变量,会有内存屏障,指令重排序时就不能把后面的指令重排序到内存屏障之前的位置,这样就保证不同的代码块只能串行执行,不能同步执行,这样就保证了线程的有序性。
volatile缺点
volatile不能保证原子性,volatile可以配合synchronized保证原子性。