- 相關(guān)推薦
解析Java中volatile關(guān)鍵字
解析Java中volatile關(guān)鍵字
在java多線程編程中經(jīng)常volatile,有時候這個關(guān)鍵字和synchronized 或者lock經(jīng)常有人混淆,具體解析如下:
在多線程的環(huán)境中會存在成員變量可見性問題: java的每個線程都存在一個線程棧的內(nèi)存空間,該內(nèi)存空間保存了該線程運行時的變量信息,當(dāng)線程訪問某一個變量值的時候首先會根據(jù)這個變量的地址找到對象的堆內(nèi)存或者是棧堆存(原生數(shù)據(jù)類型)中的具體的內(nèi)容,然后把這個內(nèi)同賦值一個副本保存在本線程的線程棧中,緊接著對這個變量的一切操作在線程完成退出之前都和堆棧內(nèi)存中的變量內(nèi)容是沒有關(guān)系的,操作的是自己線程棧中的副本。當(dāng)操作完后會把操作完的結(jié)果寫回到主內(nèi)存中。假如有兩個線程A和B,同事操作某一個變量x;A對x進行了加1操作,那么B獲取的副本可能是x加1后的結(jié)果,也可能是x;為了保證獲取內(nèi)存中最新的數(shù)據(jù)變量 需要加上 volatile 關(guān)鍵字,這樣在每次對x進行操作的時候都會去檢查下線程棧中的變量的值是不是和住內(nèi)存中變量的值一樣,如果不一樣會重新load。
eg:
public class ThreadSee { //t1線程會根據(jù)flag的值做對應(yīng)的.操作,主線程會更改t1的值 public static void main(String[] args) throws InterruptedException { ThReadTest th= new ThReadTest(); Thread t1 = new Thread(th); t1.start(); Thread.sleep(1000); th.changeFlag(); Thread.sleep(2000); System.out.println(th.getFlag()); } } class ThReadTest implements Runnable{ //線程訪問變量時會把其load到對應(yīng)的線程棧中,每次操作時都要獲取內(nèi)存中最新的數(shù)據(jù) private volatile boolean stopflag; @Override public void run() { int i=0; while(!stopflag){ i++; System.out.println("=="+Thread.currentThread().getName()); } System.out.println("Thread finish:"+i); } public void changeFlag(){ this.stopflag=true; System.out.println(Thread.currentThread().getName()+"***********"); } public boolean getFlag(){ return stopflag; } }
上述代碼如果去掉volatile,會一直死循環(huán)執(zhí)行下去。
但是volatile不能保證線程安全的同步
eg:
public class ThreadSave implements Runnable{ static ThreadSave sync = new ThreadSave(); static volatile int j=0; //Lock lock =new ReentrantLock(); public void inscane(){ // lock.lock(); for(int i=0;i<10000000;i++){ j++; } // lock.unlock(); } @Override public void run() { inscane(); } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(sync); Thread t2 = new Thread(sync); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(j); } }
根據(jù)上述代碼執(zhí)行的結(jié)果不是預(yù)期20000000,
因為對于volatile修飾的變量,jvm虛擬機只是保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的。
例如假如線程1,線程2 在進行線程棧與主內(nèi)存read,load 操作中,發(fā)現(xiàn)主內(nèi)存中count的值都是5,那么都會加載這個最新的值
在線程1堆count進行修改之后,會write到主內(nèi)存中,主內(nèi)存中的count變量就會變?yōu)?;
線程2由于已經(jīng)進行read,load操作,在進行運算之后,也會更新主內(nèi)存count的變量值為6;
導(dǎo)致兩個線程及時用volatile關(guān)鍵字修改之后,還是會存在并發(fā)的情況。
綜上所述:
volatile只會保證線程去做一個檢查當(dāng)前線程棧的變量值和主內(nèi)存中數(shù)據(jù)值是否一樣的這么一個動作,只此而已。而lock或者是synchronized 會保證某一時刻只有單個線程進入該方法,從而確保其線程安全性。
所以在如果多個線程去修改一個volatile變量那么沒有實際的邏輯意義。如果一個線程去修改其他的線程依賴修改的變量值,此時是有作用的。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
【解析Java中volatile關(guān)鍵字】相關(guān)文章:
理解java中的關(guān)鍵字04-02
Java編程中this關(guān)鍵字與super關(guān)鍵字的使用方法04-02
PHP中this關(guān)鍵字03-28
java語言源碼解析03-30
java讀取解析xml文件實例12-01