1. 程式人生 > >Java中volatile的作用以及用法

Java中volatile的作用以及用法

找了很多資料,包括《Java併發程式設計實戰》,綜合一下各家的說法就是:

volatile讓變數每次在使用的時候,都從主存中取。而不是從各個執行緒的“工作記憶體”。

volatile具有synchronized關鍵字的“可見性”,但是沒有synchronized關鍵字的“併發正確性”,也就是說不保證執行緒執行的有序性。

也就是說,volatile變數對於每次使用,執行緒都能得到當前volatile變數的最新值。但是volatile變數並不保證併發的正確性。

看下面的例子:

假如count變數是volatile的。執行緒1,執行緒2 在進行read,load 操作中,發現主記憶體中count的值都是5,那麼都會載入這個最新的值線上程1堆count進行修改之後,會write到主記憶體中,主記憶體中的count變數就會變為6,執行緒2由於已經進行read,load操作,在進行運算之後,也會更新主記憶體count的變數值為6,導致兩個執行緒及時用volatile關鍵字修改之後,還是會存在併發的情況。

--------------------------------------

綜合了查詢到的資料,上面的解釋,還算理解的過去。但是《java併發程式設計實戰》上的例子,就不是很明白了。

看下面

----------------------------------------------------------------

java併發程式設計實戰上說,如果變數不是volatile的,那麼在被其他執行緒修改之後,之前的執行緒是不會感知到的。但是下面的程式碼,asleep被修改了之後,其他四個執行緒卻都停止了輸出。不明白是怎麼回事。

  1. package comz;  
  2. /** 
  3.  * 這段程式碼雖然沒有volatile,但是另外的執行緒設定為true的時候,其他的四個執行緒依然停止了執行。 與書上不一致。為啥?
     
  4.  * 書上說,如果不是volatile的,則另外的執行緒更新這個值的時候,其他的執行緒是不會感知到的。所以其他執行緒就不會停止執行。 
  5.  * @author naughty 
  6.  *  
  7.  */
  8. class T {  
  9.     publicstaticboolean asleep = false;  
  10.     publicstaticvoid main(String[] args) throws InterruptedException {  
  11.         for (int i = 0; i < 4; i++) {  
  12.             new Thread(new Runnable() {  
  13.                 @Override
  14.                 publicvoid run() {  
  15.                     judge();  
  16.                 }  
  17.             }).start();  
  18.         }  
  19.         Thread.sleep(3000);  
  20.         new Thread(new Runnable() {  
  21.             publicvoid run() {  
  22.                 asleep = true;  
  23.                 System.out.println("end");  
  24.             }  
  25.         }).start();  
  26.     }  
  27.     publicstaticvoid judge() {  
  28.         while (!asleep) {  
  29.             try {  
  30.                 Thread.sleep(500);  
  31.             } catch (InterruptedException e) {  
  32.                 // TODO Auto-generated catch block
  33.                 e.printStackTrace();  
  34.             }  
  35.             CMS();  
  36.         }  
  37.     }  
  38.     publicstaticvoid CMS() {  
  39.         System.out.println("@");  
  40.     }  
  41. }  

----------------------------

那麼,我們這裡說執行緒要先拷貝變數到自己的工作記憶體,然後再使用。在這裡,什麼是執行緒的工作記憶體呢?

看看JLS(java語言規範)對執行緒工作記憶體的描述,執行緒的working memory只是cpu的暫存器和快取記憶體的抽象描述。