1. 程式人生 > >架構師之路第一天

架構師之路第一天

Volatile關鍵字

用處:Volatile修飾的變數能夠在多執行緒之間可見。也就是可用此關鍵字實現變數在多執行緒之間的資料一致性。

在沒有Volatile之前,是通過在變數或者方法上面進行加鎖實現的。那樣的話,效率不高。

案例:


public class VolatileTest extends Thread{
	private boolean flag = true;
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {
		while(flag)
		{	
		}
		System.out.println("執行緒結束");
	}
	public static void main(String[] args) throws Exception {
		VolatileTest test = new VolatileTest();
		test.start();
		Thread.sleep(3000);
		test.setFlag(false);
		System.out.println("已經將falg設定為了false");
		System.out.println(test.isFlag());
	}
}

上述的結果為:

已經將falg設定為了false
false

並沒有出現執行緒結束的列印,也就是說執行緒並沒有正常結束。

原因分析:在主執行緒中開啟了一個test的執行緒,然後主執行緒休眠了3秒。這個時候再去到主執行緒的test引用賦值了flag為false,這個賦值的flag並沒有影響到test執行緒中的flag變數的(這個機制是JDK的一個優化策略:開啟一個新的執行緒會開闢一個記憶體用來儲存執行緒中用到的變數,這個變數是該執行緒特有的。在主執行緒中用到的變數還是主執行緒的變數,不會影響到其他執行緒的變數)。

解決辦法:將flag這個變數使用Volatile關鍵字修飾,使得該變數可以在多執行緒之間顯示,從而實現上述的正確結果。因為volitile修飾的變數後,該變數改變會強制到主記憶體去讀取,並將改變後值載入到開闢的記憶體中。如下:

public class VolatileTest extends Thread{
	private volatile boolean flag = true;
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {
		while(flag)
		{	
		}
		System.out.println("執行緒結束");
	}
	public static void main(String[] args) throws Exception {
		VolatileTest test = new VolatileTest();
		test.start();
		Thread.sleep(3000);
		test.setFlag(false);
		System.out.println("已經將falg設定為了false");
		System.out.println(test.isFlag());
	}
}

允許的結果為:

已經將falg設定為了false
執行緒結束
false

注意:Volatile是沒有原子性的,在多個執行緒訪問的時候還是會出現資料不同步的現象。Volatile只是保證其修飾的變數在多執行緒之間顯示,也就是能夠在多個執行緒之間訪問得到,並且對其能夠進行操作受到影響的,並不能保證其資料的一致性的。如果需要保證其原子性,那麼這個時候可以使用關鍵詞AtomicInteger