首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

一个java volatile测试揭开的圈套

2013-03-29 
一个java volatile测试揭开的陷阱package test.threadpublic class TestVolatile {private volatile int

一个java volatile测试揭开的陷阱
package test.thread;public class TestVolatile { private volatile int n1=0;//volatile private int n2=0; public static void main(String[] a) { new TestVolatile().test(); new TestVolatile().test(); } public void test() { Thread t1 = new Thread() { public void run() { do{// System.out.println("---"); }while(n2-n1<=0); System.out.println("n2>n1"); } }; Thread t2 = new Thread() { public void run() { for (;n1 < Integer.MAX_VALUE; ) { ++n1; ++n2; } System.out.println("stoped"); } }; t1.start(); t2.start(); }}

?

对于n1,n2,只有t2线程对其修改,并且n1总是先于n2自增,所以有:

n1在任何时刻都大于或等于n2

然后在t1线程里,根据表达式的顺序,先读取n2,再读取n1,因为都在增大,所以后读取的应该比先读取的大,并且n1是volatile的,能保证读到的是最新值,所以应该有

在t1线程里,表达式n2-n1<=0恒成立

但事实却并非如此,此测试会输出“n2>n1”(注意:需要让JVM在server模式下进行测试)

此问题困扰了我几个月,百思不得其解,最近这几天,在一篇深入分析volatile的文章(本文末尾有链接)里找到了答案

其实volatile并不保证所有情况都不进行重排序,像下面两种情况,是允许指令重排序的

1、普通变量的读/写操作,然后volatile变量的读操作

2、volatile变量的写操作,然后普通变量的读/写操作

再回过来看上面的测试,

++n1;可以看作是temp=n1;temp=temp+1;n1=temp;最后执行的肯定是一个写入操作,接着是++n2;是普通操作,按照上面的规则2,此处允许重排序,n2的写入若被排到了n1的写入之前,那么n1>=n2就不是恒成立的了;

不仅t2线程这出了问题,t1线程里也出了问题

n2-n1<=0,先是读取普通变量n2,然后是volatile变量n1的读操作,按照规则1,也是可以重排序的,如果先读取了n1,接着t2线程让n1、n2都增加了,t1然后再读了n2,那么完全有可能n2大于n1

至此问题已经分析清楚了,volatile并不严格保证指令不被重排序。

既然如此,这算不算是volatile的设计缺陷呢?

其实这不能算缺陷,因为该测试中,对volatile的使用本身就是不合适的(没加volatile的变量对其它线程的可见性本身就有问题,其值是不确定的),没必为一个错误的用法作出严格的保证

若想详细了解volatile,请参考此文:http://www.infoq.com/cn/articles/java-memory-model-4

在此感谢作者?程晓明,解开了我几个月来的困惑。

?

?

热点排行