网上关于java volatile的资料已经不少了,但搜了好久也没看到谁用代码很好地验证过使不使用volatile的差异。最近自己写了个测试,意外的看到了两者的明显区别,为什么说意外呢,因为根据我的测试,在32位的JVM(以下均指oracle官方的JVM)上是看不出差异的,也就是说32位的JVM上,不会发生因为没使用volatile而引起的多个线程读写公共变量不同步问题(有误,见末尾的补充),偶然在64位的JVM上跑了下,问题一下出现了。我一直是用32位的JVM做测试,一直把问题的不出现归结于概率太低,所以没遇到,以致于我写了下面这个把这个小概率事件成倍放大的测试,但还是没看出差异,后来放到64位的JVM上,问题一下就出现了,程序直接不退出了
/** * volatile关键字的测试 * @author trytocatch * @date 2013-1-7 */ public class Volatile { long time1;//test运行时,使用两独立的变量来保存时间,避免因使用同步而对t1,t2造成影响 long time2; volatile boolean boolValue=true;//volatile public static void main(String[] args) throws InterruptedException{ int size=5000;//测试个数 Volatile vs[]=new Volatile[size]; long timeSum = 0; for(int n=0;n<size;n++) (vs[n]=new Volatile()).test(); // Thread.sleep(1000); for(int n=0;n<size;n++){//统计出,所有线程从boolValue变为false到while(boolValue)跳出所花时间的总和 timeSum+=vs[n].time2 - vs[n].time1; System.out.print(n+"\t"+vs[n].time2 +'\t' + vs[n].time1+'\t'+(vs[n].time2 - vs[n].time1)+'\n'); } System.out.println("响应时间总和(毫微秒):"+timeSum); long time1,time2; time1 = System.nanoTime(); // Thread.yield(); time2 = System.nanoTime(); System.out.println(time2-time1);//顺序执行两条语句的时间间隔,供参考 } public void test() throws InterruptedException{ Thread t2=new Thread(){ public void run(){ while(boolValue) ; time2 = System.nanoTime(); } }; Thread t1=new Thread(){ public void run(){ time1 = System.nanoTime(); boolValue=false; } }; t2.start(); Thread.yield(); t1.start(); t1.join();//保证一次只运行一个测试,以此减少其它线程的调度对 t2对boolValue的响应时间 的影响 t2.join(); } }
至于我上面说的,在32位JVM上,不会发生因为没使用volatile而引起的多个线程读写公共变量不同步问题,因为我把测试的size增加到50000也没出一个问题,而且一放到64位JVM上,问题立马出现,所以可以断定是JVM的差异,至于为什么32位的JVM不会出问题,难道是它根本没做优化?希望JVM高手指点
补充:今天在网上看到个资料,64位jvm只有server模式(server模式会进行更多的优化),32位JVM默认使用client模式,我将32位JVM设置为server模式后,问题同样出现了,平时不出现,是因为它根本就没做这方面的优化,所以,“在32位JVM上,不会发生因为没使用volatile而引起的多个线程读写公共变量不同步问题”的说法有误,只是参数不同而已