java之用volatile和不用volatile的區別

轉載:http://blog.csdn.net/feier7501/article/details/20001083java

在當前的Java內存模型下,線程能夠把變量保存在本地內存(好比機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能形成一個線程在主存中修改了一個變量的值,而另一個線程還繼續使用它在寄存器中的變量值的拷貝,形成數據的不一致。 ide



要解決這個問題,只須要像在本程序中的這樣,把該變量聲明爲volatile(不穩定的)便可,這就指示JVM,這個變量是不穩定的,每次使用它都到主存中進行讀取。通常說來,多任務環境下各任務間共享的標誌都應該加volatile修飾。 


spa

Volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。並且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任什麼時候刻,兩個不一樣的線程老是看到某個成員變量的同一個值。 .net

 

用volatile和不用volatile的區別,運行一下,就知道了。線程

不用volatile:code

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. package com.keyword;  
  2.   
  3. public class TestWithoutVolatile {  
  4.     private static boolean bChanged;  
  5.   
  6.     public static void main(String[] args) throws InterruptedException {  
  7.         new Thread() {  
  8.   
  9.             @Override  
  10.             public void run() {  
  11.                 for (;;) {  
  12.                     if (bChanged == !bChanged) {  
  13.                         System.out.println("!=");  
  14.                         System.exit(0);  
  15.                     }  
  16.                 }  
  17.             }  
  18.         }.start();  
  19.         Thread.sleep(1);  
  20.         new Thread() {  
  21.   
  22.             @Override  
  23.             public void run() {  
  24.                 for (;;) {  
  25.                     bChanged = !bChanged;  
  26.                 }  
  27.             }  
  28.         }.start();  
  29.     }  
  30.   
  31. }  


運行後,程序進入死循環了,一直在運行。blog

 

 

用volatile:ip

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. package com.keyword;  
  2.   
  3. public class TestWithVolatile {  
  4.     private static volatile boolean bChanged;  
  5.   
  6.     public static void main(String[] args) throws InterruptedException {  
  7.         new Thread() {  
  8.   
  9.             @Override  
  10.             public void run() {  
  11.                 for (;;) {  
  12.                     if (bChanged == !bChanged) {  
  13.                         System.out.println("!=");  
  14.                         System.exit(0);  
  15.                     }  
  16.                 }  
  17.             }  
  18.         }.start();  
  19.         Thread.sleep(1);  
  20.         new Thread() {  
  21.   
  22.             @Override  
  23.             public void run() {  
  24.                 for (;;) {  
  25.                     bChanged = !bChanged;  
  26.                 }  
  27.             }  
  28.         }.start();  
  29.     }  
  30.   
  31. }  


程序輸出!=,而後立刻退出。內存

 

可是,不少狀況下,用不用volatile,感受不出什麼區別,何時要用volatile呢?看看JDK裏使用volatile的類。get

好比java.util.regex.Pattern裏的變量:

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. private transient volatile boolean compiled = false;  


還有,java.lang.System的變量:

 

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. private static volatile Console cons = null;  


通常就是初始化的時候,須要用到volatile。

 

java.util.Scanner裏的變量,如:

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. private static volatile Pattern boolPattern;  
  2. private static volatile Pattern separatorPattern;  
  3. private static volatile Pattern linePattern;  


初始化boolPattern的代碼:

 

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. private static Pattern boolPattern() {  
  2.         Pattern bp = boolPattern;  
  3.         if (bp == null)  
  4.             boolPattern = bp = Pattern.compile(BOOLEAN_PATTERN,  
  5.                                           Pattern.CASE_INSENSITIVE);  
  6.         return bp;  
  7. }  

 

 

上面的狀況,可使用synchronized來對boolPattern加鎖,可是synchronized開銷比volatile大,volatile可以勝任上面的工做。

 

volatile不保證原子操做,因此,很容易讀到髒數據。

 

使用建議:在兩個或者更多的線程訪問的成員變量上使用volatile。當要訪問的變量已在synchronized代碼塊中,或者爲常量時,沒必要使用。 

相關文章
相關標籤/搜索