您的位置:首页 > 其它

为了防止程序重排序,慎用volatile 推荐

2014-11-16 11:21 169 查看
之前在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更好的同步主内存跟工作内存的值,那样的话,在高并发下,就能更快的提高程序性能了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  volatile JVM 重排序