首页 > 代码库 > 为了防止程序重排序,慎用volatile
为了防止程序重排序,慎用volatile
之前在InfoQ看到一篇关于java重排序的一篇文章,觉得里面有些知识写得太绝对了,于是想通过实际程序来说明一下:
关于java重排序,这里就不做介绍了,我们知道JVM底层封装了与OS的交互,它内部有自己的一套类似于OS的内存模型,程序重排序的设计思路基本上是来源于OS。下面直接入正题吧!
我们知道JVM给每个线程分配了自己的内存空间,也就是说在变量存储方面,分为主内存和线程工作内存,也就是说,所有线程共享主内存,每个线程都有自己的工作内存。程序执行的时候是去工作内存里面取值还是去主内存里面取值呢?下面以代码为例:
public class DemoWork { private boolean stop=false; private boolean start=true; public void workThread() throws InterruptedException{ Thread workThread=new Thread(new Runnable() { private int i=0; @Override public void run() { // TODO Auto-generated method stub while(!stop){ i++; /*try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } start=false; } }); workThread.start(); Thread.sleep(1000); stop=true; Thread printThread=new Thread(new Runnable() { private int i=0; @Override public void run() { // TODO Auto-generated method stub while(stop&&start){ System.out.println("stop is:"+stop); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); printThread.start(); } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub DemoWork dw=new DemoWork(); dw.workThread(); } }
上面的代码是不会停下来的,但是如果把sleep那段代码的注释去掉程序就能停下来了,这是什么原因呢?我的理解是:因为线程printThread是能正常执行的,所以有两种可能:
线程workThread里面工作线程stop变量值没有收到主存的同步,而它一直取的是自己工作线程里面的stop值
主线程更新stop没有更新主内存,以至于主内存里面保存的stop值一直是false
以上第二点我觉得是可以排除的,因为线程printThread里面的值stop值是true,所以造成以上情况第一点的可能性大一点,那为什么把workThread里面的睡眠去掉之后程序又能正常退出呢?那就应该是在执行这些语句的时候主内存更新了工作内存的缘故了(执行打印语句也会推出,至于这里面的原因是什么,暂时还没看到相关的资料,可能跟JVM的重排序规则有关系,但是规则到底是怎样的呢?),接下来我们来说说volatile。
volatile:
(适用于Java所有版本)读和写一个volatile变量有全局的排序。也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。(但是并不保证经常读写volatile作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。
(适用于Java5及其之后的版本)volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁[7]。
也就是说在上面workThread线程sleep代码段注释的情况下,我们可以使用volatile来修饰stop变量,这样的话就能强制workThread线程去主内存里面取stop的值了,但是这样做的话在高并发现会造成性能问题。之前看了很多的开源代码,里面解决以上主内存与工作内存不同步的方式基本上是采用volatile修饰变量解决的。我在想,既然volatile在并发情况下会造成性能问题,在workThread循环快里面执行什么类型的代码快能方便JVM更好的同步主内存跟工作内存的值,那样的话,在高并发下,就能更快的提高程序性能了。
本文出自 “陈砚羲” 博客,请务必保留此出处http://chenyanxi.blog.51cto.com/4599355/1577101
为了防止程序重排序,慎用volatile