網上關於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而引起的多個線程讀寫公共變量不同步問題”的說法有誤,只是參數不同而已