詳解volatile 關鍵字與內存可見性


    先來看一個例子:

  1. public class VolatileTest {  
  2.     
  3.     public static void main(String[] args) {  
  4.         ThreadDemo td = new ThreadDemo();  
  5.         new Thread(td).start();  
  6.     
  7.         while (true) {  
  8.             if (td.isFlag()) {  
  9.                 System.out.println("================");  
  10.                 break;  
  11.             }  
  12.         }  
  13.     }  
  14. }  
  15.     
  16. class ThreadDemo implements Runnable {  
  17.     private boolean flag = false;  
  18.     
  19.     @Override  
  20.     public void run() {  
  21.         try {  
  22.             Thread.sleep(200);  
  23.         } catch (InterruptedException e) {  
  24.     
  25.         }  
  26.         flag = true;  
  27.         System.out.println("falg=" + flag);  
  28.     }  
  29.     
  30.     public boolean isFlag() {  
  31.         return flag;  
  32.     }  
  33.     
  34.     public void setFlag(boolean flag) {  
  35.         this.flag = flag;  
  36.     }  
  37. }  

兩個線程,一個改flag的值,主線程做判斷,結果:

 

 

主線程的flag貌似還是false,按理通過Runnable創建的線程訪問的應該是共享數據,那為什么會出現這種情況?這就涉及到內存可見性。

JVM會為每個線程分配一個獨立緩存提高效率。上述例子中我們先在主存中開辟一塊內存,如圖:

 

那么這個兩個線程,一個是讀(主線程),一個是寫(線程1),我們讓線程1睡了200ms,說明,線程1先執行,每個線程都有一個獨立的緩存,也就是說當線程1需要對主存的共享數值進行改變,它需要先把這個flag復制一份到緩存區中,

 

然后修改,將來再把這個值寫回主存去,在寫之前,主線程來了,它要讀取現在在內存里面的值,現在是false,當然有一種情況,就是線程1在某個機會將flag=true寫回去,

 

 

 

當時主線程用了while(true),這句話調用了系統底層代碼,效率極高,高到主線程沒有機會再次讀取內存,這就是線程對共享數據操作的不可見。

內存可見性問題:當多個線程操作共享數據時,彼此不可見。

如何解決?同步鎖。

 

 

 

但是用了鎖,代表效率極低,但是我現在我不想加鎖,但是有存在內存可見性的問題,我該怎么辦?

關鍵字volatile:當多個線程進行操作共享操作時,可以保證內存中的數據可見。(內存柵欄,實時刷新)

我們可以認為它是直接在主存操作的,這個實時刷新的操作相比不加,性能略低,但是比加鎖的效率顯然高很多,低在哪?加了這關鍵字,JVM就不能進行指令重排序,無法優化代碼執行

  1. public class VolatileTest {  
  2.     
  3.     public static void main(String[] args) {  
  4.         ThreadDemo td = new ThreadDemo();  
  5.         new Thread(td).start();  
  6.     
  7.         while (true) {  
  8.             if (td.isFlag()) {  
  9.                 System.out.println("================");  
  10.                 break;  
  11.             }  
  12.         }  
  13.     }  
  14. }  
  15.     
  16. class ThreadDemo implements Runnable {  
  17.     private volatile boolean flag = false;  
  18.     
  19.     @Override  
  20.     public void run() {  
  21.         try {  
  22.             Thread.sleep(200);  
  23.         } catch (InterruptedException e) {  
  24.     
  25.         }  
  26.         flag = true;  
  27.         System.out.println("falg=" + flag);  
  28.     }  
  29.     
  30.     public boolean isFlag() {  
  31.         return flag;  
  32.     }  
  33.     
  34.     public void setFlag(boolean flag) {  
  35.         this.flag = flag;  
  36.     }  
  37. }  

 

 

volatile相對synchronized是一種輕量級同步策略。但是注意:

  1. volatile不具備互斥性
  2. volatile不能保證變量的原子性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM