程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Volatile關鍵字詳解

Volatile關鍵字詳解

編輯:JAVA編程入門知識
  • 簡介

  在java中,每個線程有一塊工作內存區,其中存放這被所有線程共享的主內存中變量值的拷貝。當線程執行時,它在自己的工作內存中操作這些變量。為了獲取一個共享變量,一個線程先獲取鎖定並清除它的工作內存區,這就保證了該共享變量從所有的線程的共享主內存區正確的裝入到線程的工作內存區,當線程解鎖時保證該工作內存區的變量的值寫回到共享主內存區。

  線程工作內存和主內存的交互圖如下:

  從上圖中可以看出,主內存和線程工作內存間的數據傳輸與線程工作內存和線程執行有一定的時間間隔,而且每次所消耗的時間可能還不相同,這樣就存在線程操作的數據的不一致性。由於每個線程都有自己的線程工作內存,因此當一個線程改變自己工作內存中的數據的時候,對於其他系統來說可能是不可見的。因此,使用volatile關鍵字迫使所有的線程均讀寫主內存中對應的變量,從而使得volatile關鍵字修飾的變量在多線程間可見。

  volatile修飾的變量,jvm虛擬機只是保證從主內存加載到線程工作內存的值是最新的。

  聲明為volatile的變量具有如下特性:

  1、其他線程對變量的修改可以即時反映在當前線程中。

  2、確保當前線程對volatile變量的修改,能即時的寫回到共享主內存中,並被其他線程所見。

  3、使用volatile修飾的變量,編譯器會保證其有序性。

  • volatile分析

  用在多線程,同步變量。 線程為了提高效率,將某成員變量(如A)拷貝了一份(如B),線程中對A的訪問其實訪問的是B。只在某些動作時才進行A和B的同步。因此存在A和B不一致的情況。volatile就是用來避免這種情況的。volatile告訴jvm, 它所修飾的變量不保留拷貝,直接訪問主內存中的(也就是上面說的A) 

  下面一個測試例子:

 public class MyThread extends Thread{
     private volatile  boolean stop = false;//確保stop在多線程中可見
     
     public void stopMe(){
         stop = true;
         System.out.println("stopMe"+System.currentTimeMillis());
     }
 
     @Override
     public void run() {
         int i = 0;
         while(!stop){
             i++;
         }
         System.out.println("run"+System.currentTimeMillis());
         System.out.println("stop thread");
     }
     
 }

  如果stop沒有被聲明為volatile類型,那麼線程在執行run的時候是檢查自己的工作內存的副本,不能得知其他線程對stop的修改,因此線程無法結束。但是將stop被聲明為volatile類型,那麼在其他線程修改stop後,線程會立刻知道,則會跳出循環,正常結束。

  volitile與synchronized的區別

  Volatile一般情況下不能代替sychronized,因為volatile不能保證操作的原子性,即使只是i++,實際上也是由多個原子操作組成:read i; inc; write i,假如多個線程同時執行i++,volatile只能保證他們操作的i是同一塊內存,但依然可能出現寫入髒數據的情況。如果配合Java 5增加的atomic wrapper classes,對它們的increase之類的操作就不需要sychronized。

  synchronized獲得並釋放監視器——如果兩個線程使用了同一個對象鎖,監視器能強制保證代碼塊同時只被一個線程所執行。volatile只是在線程內存和“主”內存間同步某個變量的值,而synchronized通過鎖定和解鎖某個監視器同步所有變量的值。顯然synchronized要比volatile消耗更多資源。

  在使用volatile關鍵字時要慎重,並不是只要簡單類型變量使用volatile修飾,對這個變量的所有操作都是原來操作,當變量的值由自身的上一個決定時,如n=n+1、n++ 等,volatile關鍵字將失效,只有當變量的值和自身上一個值無關時對該變量的操作才是原子級別的,如n = m + 1,這個就是原級別的。所以在使用volatile關鍵時一定要謹慎,如果自己沒有把握,可以使用synchronized來代替volatile。 volatile 變量不會像鎖那樣造成線程阻塞,在某些情況下,如果讀操作遠遠大於寫操作,volatile 變量還可以提供優於鎖的性能優勢。

  • 原理

  如果對聲明了Volatile變量進行寫操作,JVM就會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫回到系統內存。但是就算寫回到內存,如果其他處理器緩存的值還是舊的,再執行計算操作就會有問題,所以在多處理器下,為了保證各個處理器的緩存是一致的,就會實現緩存一致性協議,每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存的值是不是過期了,當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器要對這個數據進行修改操作的時候,會強制重新從系統內存裡把數據讀到處理器緩存裡。

  lock前綴指令實際上相當於一個內存屏障(也成內存柵欄),內存屏障會提供3個功能:

 

  1)它確保指令重排序時不會把其後面的指令排到內存屏障之前的位置,也不會把前面的指令排到內存屏障的後面;即在執行到內存屏障這句指令時,在它前面的操作已經全部完成;

 

  2)它會強制將對緩存的修改操作立即寫入主存;

 

  3)如果是寫操作,它會導致其他CPU中對應的緩存行無效。

 

    把對volatile變量的單個讀/寫,看成是使用同一個監視器鎖對這些單個讀/寫操作做了同步。下面我們通過具體的示例來說明,請看下面的示例代碼:

 class VolatileFeaturesExample {
     volatile long vl = 0L;  //使用volatile聲明64位的long型變量
     public void set(long l) {
         vl = l;   //單個volatile變量的寫
     }
     public void getAndIncrement () {
         vl++;    //復合(多個)volatile變量的讀/寫
     }
     public long get() {
         return vl;   //單個volatile變量的讀
     }
 }

  假設有多個線程分別調用上面程序的三個方法,這個程序在語意上和下面程序等價:

 class VolatileFeaturesExample {
     long vl = 0L;               // 64位的long型普通變量
     public synchronized void set(long l) {     //對單個的普通 變量的寫用同一個監視器同步
         vl = l;
     }
     public void getAndIncrement () { //普通方法調用
         long temp = get();           //調用已同步的讀方法
         temp += 1L;                  //普通寫操作
         set(temp);                   //調用已同步的寫方法
     }
     public synchronized long get() { 
     //對單個的普通變量的讀用同一個監視器同步
         return vl;
     }
 }
  • Volatile與synchronized的區別:

  synchronized獲得並釋放監視器——如果兩個線程使用了同一個對象鎖,監視器能強制保證代碼塊同時只被一個線程所執行——這是眾所周知的事實。但是,synchronized也同步內存:事實上,synchronized在“ 主”內存區域同步整個線程的內存。

  volatile只是在線程內存和“主”內存間同步某個變量的值,而synchronized通過鎖定和解鎖某個監視器同步所有變量的值。顯然synchronized要比volatile消耗更多資源。

  Volatile只能保證可見性和有序性,對任意單個volatile變量的讀/寫具有原子性,所有在對volatile變量進行操作的時候要保證其操作是原子性,否則就要加鎖來保證原子性。

  • Volatile的使用必須滿足的條件

  1、對變量的寫操作不依賴於當前值。

  2、該變量沒有包含在具有其他變量的不變式中。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved