Java知识整理(10)—— volatile

为什么要用volatile?
为什么要用volatile,它可以解决两个问题:
  • 保证不同线程对同一个变量进行操作时的可见性问题;
  • 禁止进行指令重排序。
1.可见性问题
可见性问题是JAVA并发编程中的基本概念:
官方定义如下:
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看的见。
为什么出现可见性问题?
在并发编程中,不同的线程在不同的cpu执行着,而CPU的缓存机制长这样:
若线程1运行在CPU1上,线程2运行在CPU2上,当线程1修改了某一个对象的值时,可能会缓存在cache1(若cache1满了则缓存在cache2中,以此内推),没有写进主存,那线程2就无法读到最新的数据,因此不能实现可见性。
volatile的作用就是被volatile修饰的变量被修改时强制立即写进主存中,让cache失效,这样其他读该变量的线程就可以立即读到最新的值。这就是volatile的作用之一。
2.有序性问题
有序性是Java并发编程中的另一概念,有序性指程序执行的顺序按照代码的先后顺序进行,而指令重排序是指在执行程序时,编译器和处理器为了提高代码执行效率,会对没有数据依赖的代码行进行重排序,在保证程序最终结果不受影响的情况下更高效地完成代码的运行。不管怎么重排序,单线程执行结果不会被改变,但会影响多线程并发执行的正确性。
举个栗子:
线程1执行下面这段代码: 线程2执行下面这段代码:
Object o = new Object(); //1 if(flag){ //3
boolean flag = true; // 2 o.method(); //4
}
 
说明:因为1和2不存在数据依赖,若重排序2比1先执行,那么3和4就会出错。
在Java里面,可以通过volatile关键字来保证一定的“有序性”。volatile关键字禁止指令重排序,在代码前面添加内存屏障从而禁止指令重排。
volatile关键字禁止指令重排序有两层意思:
  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
如果一个变量被volatile修饰,JMM会在写入这个字段之后插入一个wirite-Barrier指令,并在读这个字段之前插入一个read-barrier指令。具体如何实现的请看这篇博客,写的很好:http://www.infoq.com/cn/articles/java-memory-model-4/
volatile有哪些不行的?
不能保证原子性。它仅能够对原始变量操作保证原子性,不能对复合操作保证原子性。只有借助synchronized和Lock。
优秀参考博文:
 
原文地址:https://www.cnblogs.com/Gabby/p/6752225.html