1. volatile的做用java
a.volatile关键字能够简单保持赋值和返回操做的原子性,弱同步。编程
好比:读取和写入long和double不是原子性的操做,jvm会把64位(long和double)的读取和写入看成两个分离的32位操做来执行。这就产生了在一个读取和写入操做中间发生上下文切换,从而致使不一样的任务能够看到不正确的结果 ,可是若是当你定义long和double变量时,使用volatile关键字,就会获得(赋值和返回操做)原子性。 缓存
b.volatile还能够确保应用中的可视性。jvm
若是你将一个域声明为volatile,那么只要对这个域产生了写操做,那么全部的读操做就均可以看到这个修改。即使使用了本地缓存,状况也确实若是,volatile域会当即被写入主存,而读取操做就发生在主存中。性能
其实同步也会致使向主存中刷新,所以若是一个域彻底有synchronnized方法或语句块来防御,那就没必要将其设置为是volatile。学习
若是你将一个域定义为volatile,那么它会告诉编译器不要执行任何移除读取和写入操做的优化,这些操做的目的是用线程中的局部变量维护对这个域的精准同步。优化
注意:若是一个域的值依赖于它以前的值时,(例如递增一个计数器),volatile就没法操做了,示例:线程
package mutex.conflict; import java.util.concurrent.*; public class AtomicityTest implements Runnable { private int i = 0; public int getValue() { return i; } private synchronized void evenIncrement() { i++; i++; } public void run() { while(true) evenIncrement(); } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomicityTest at = new AtomicityTest(); exec.execute(at); while(true) { int val = at.getValue(); if(val % 2 != 0) { System.out.println(val); System.exit(0); } } } } /* Output: (Sample) 191583767 *///:~
输出:code
191583767blog
该程序的主要目的是找到奇数值并终止。
尽管return i是原子性操做,可是缺乏同步使其数值能够在处于不稳定的中间状态时被读取。除此以外,因为i也不是volatile的,所以还存在可视性问题。(不过这儿就是i是volatile也不起做用)因此getValue()和eventIncrement()必须是synchronized的。就能够解决问题。
学习总结自《java编程思想》
---------------------------------------------更新于2019年6月9号--------------------------------------
如今看了这篇文章,感受总结的太浅显了。下面作一个深刻的总结。
volatile的主要做用是:保证了变量修改的可见性,即一个线程对该变量的修改,对另一个线程立马可见。
那么问题来了,为何不用这个关键字修饰,就不可用呢?主要是两个缘由
1.缓存致使内存不可见。
如今的cup的运行速度远远高于内存的读写速度, 为了让cpu没必要由于等待读写内存数据而空闲。如今cpu和内存以前都会增长一道高速缓存cache, 而多核的cpu的每一个核心都有本身的cache。当数据更新只在本身的cache, 尚未更新到主存中,别的线程这个时候是不可见的。
2. 指令重排序致使不可见
指令重排序是指: cpu采用了容许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。在应用程序就是,提升代码执行性能,指令的执行顺序被编译器或者运行时环境调整顺序的现象。重排序分为编译期重排序和运行期重排序,编译期重排序指的是编译期在编译源代码的时候对代码的执行顺序进行分析,在不管怎么重排序,不影响最终的执行结果的状况下,对代码进行重排序,以提升代码执行性能。运行期重排序,指的是为了提升指令流水并行执行能力,系统对机器指令执行顺序的调整。
重排序是怎么致使不可见的呢?
因为指令顺序的调整,线程B读取某个变量的时候,线程1可能尚未进行写入操做,虽然代码顺序是线程1写入代码在前面。
volatile的原理:
volatile不容许线程内部缓存,直接写入主存,而读取的时候,也是直接读取主存,不读取本身的cpu cache。
volatile 不容许指令重排。