在给 volatile关键字解析 之前 希望看本文的朋友对Java内存模型有一定的了解;不了解的朋友可以先看看Java内存模型
一、volatile关键字有什么用呢?
1、保证了不同线程访问同一个变量时的可见性,也就是说A线程修改了一个变量的值,B线程C线程能马上就能看到这个变量的最新值。
2、禁止指令的重排序。
1、volatile关键字能保证可见性吗?
看一个简单的例子:
//线程1
int i = 1;
i = 100;
//线程2
int k = i ;
如果了解Java内存模型,相信你一定可以一眼看出,在多线程的情况下,线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =100这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为100,那么在CPU1的高速缓存当中i的值变为100了,却没有立即写入到主存当中。此时线程2执行 k = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得 k 的值为0,而不是100,如果加了volatile 呢?
加上了volatile,就相当于给访问 volatile 修饰变量的线程 一双火眼金睛~
线程1会把volatile修饰的这个变量的每一个操作后的值都告诉主存,其他线程任何时候都可以读取到最新最正确的值;
2、volatile关键字能保证有序性吗?
看一个简单的例子:
//线程1
resourse = config.load(); //语句1:假设是加载一个文件完成初始化;
boolean flag = true;//语句2
//线程2
if(flag=false){
Thread.sleep(1000);
}
add(resourse);
在Java内存模型中我们举过这个例子,在多线程的情况下:如果语句2先执行了,可能导致发生异常;如果给flag变量加上volatile 关键字,volatile关键字能保证在它之前的语句1执行了,才会执行它自己语句2;再看一个单线程的例子:
a = 1;//语句1
b = 2;//语句2
volatile boolean flag = true;//语句3
a = 4;//语句4
b = 5;//语句5
我们知道由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。
并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果对语句3、语句4、语句5是可见的。
3、volatile关键字能保证原子性吗?
volatile并不能保证一个操作是原子操作,为了让一个操作是原子操作可以使用synchronized关键字和Lock锁来实现;
二、volatile 使用场景:
在多线程的情况下,需要进行读(获取)操作时可以使用volatile 关键字来修饰变量,在进行写操作时,还是需要使用synchronized关键字和Lock锁来实现。
举个简单的例子:concurrenthashmap 的 put 操作是要加锁才能保证把节点放进“桶”的时候是线程安全的,
而 get 操作是不需要加锁的,原因就是 节点 的属性 使用了volatile 关键字;