我們知道,在Java中設定變數值的操作,除了long和double類型的變數外都是原子操作,也就是說,變數值的簡單讀寫操作沒有必要進行同步。
這在JVM 1.2之前,Java的記憶體模型實作總是從主記憶體讀取變量,是不需要進行特別的注意的。而隨著JVM的成熟與最佳化,現在在多執行緒環境下volatile關鍵字的使用變得非常重要。在目前的Java記憶體模型下,執行緒可以把變數保存在本地記憶體(例如機器的暫存器)中,而不是直接在主記憶體中進行讀寫。這就可能造成一個執行緒在主記憶體中修改了一個變數的值,而另一個執行緒也繼續使用它在暫存器中的變數值的拷貝,造成資料的不一致。要解決這個問題,只需要像在本程式中的這樣,把該變數宣告為volatile(不穩定的)即可,這就指示JVM,這個變數是不穩定的,每次使用它都到主記憶體中進行讀取。一般說
來,多任務環境下各任務間共享的標誌都應該加volatile修飾。
Volatile修飾的成員變數在每次被執行緒存取時,都會強迫從共享記憶體重讀該成員變數的值。而且,當成員變數發生變化時,強迫執行緒將變化值回寫到共享記憶體。這樣在任何時刻,兩個不同的執行緒總是看到某個成員變數的同一個值。
Java語言規範中指出:為了獲得最佳速度,允許執行緒保存共享成員變數的私有拷貝,而且只當執行緒進入或離開同步程式碼區塊時才與共享成員變數的原始值比較。
這樣當多個執行緒同時與某個物件互動時,就必須注意到要讓執行緒及時的得到共享成員變數的變化。
而volatile關鍵字就是提示VM:對於這個成員變數不能保存它的私有拷貝,而應直接與共享成員變數互動。
使用建議:在兩個或更多的線程訪問的成員變數上使用volatile。當要存取的變數已在synchronized程式碼區塊中,或為常數時,不必使用。
由於使用volatile屏蔽掉了VM中必要的程式碼最佳化,所以在效率上比較低,因此請務必在必要時才使用此關鍵字。
在虛擬機器的實作中,int,char等基本型別為一個字長。而long和double佔兩個字長。在某些虛擬機器的實作中,兩個字長可能會被當作兩個原子性的單字長來操作。
如果不以volatile修飾long和double,如果多執行緒存取該變量,由於long操作的整體非原子性而導致結果混亂。
例如:int,一個執行緒寫入4,另一個寫入5. 最後肯定是4或5.而long型,可能就是個亂七八糟的數值了。