關鍵字volatile的主要作用是使變量在多個線程間可見,強制從公共堆棧中取得變量的值,而不是從線程的私有數據棧中取得變量的值。這麼說很難理解,我們直接看代碼例子來說明。
創建一個RunThread.java的類如下:
public class RunThread extends Thread{
private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("進入run了");
while(isRunning){
}
System.out.println("線程被停止了");
}
}
運行類Run代碼如下:
public class RunTest {
public static void main(String[] args){
try {
RunThread thread = new RunThread();
thread.start();
Thread.sleep(1000);
thread.setRunning(false);
System.out.println("已經賦值false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行RunTest結果如下:
進入run了
已經賦值false
從結果看來,線程內部一執行了死循環,代碼System.out.println(“線程被停止了”)並沒有被執行。這是爲什麼呢,明明已經通過 thread.setRunning(false)改變了isRunning的值啊。
這是因爲在啓動RunThread.java線程時,變量private boolean isRunning = true;存在於公共堆棧及線程的私有堆棧中,線程一直在私有堆棧中取得isRunning的值是true。而代碼thread.setRunning(false)雖然被執行,更新的確是公共堆棧中的isRunning變量值false,所以一直還是處於死循環狀態。這個問題其實就是私有堆棧中的值和公共堆棧中的值不同步造成。解決這個問題就要用到volatile關鍵字,當線程訪問isRunning這個變量時,強制從公共堆棧中進行取值。
將RunThread.java類代碼變更如下:
public class RunThread extends Thread{
volatile private volatile boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("進入run了");
while(isRunning){
}
System.out.println("線程被停止了");
}
}
再運行後,可看到運行結果:線程被終止了。
總結:使用volatile關鍵字,強制從公共內存中讀取變量,增加了實例變量在多個線程之間的可見性。
這裏將關鍵字synchronized 和 volatile進行一下比較:
1.volatile是線程同步的輕量級實現,所以volatile性能synchronized要好,並且volatile只能修飾於變量,而synchronized可以修飾方法,以及代碼塊。
2.多線程訪問volatile不會發生阻塞,而synchronized會出現阻塞;
3.volatile能保證數據的可見性,但不能保證原子性,這也是volatile最致命的缺點;而synchronized可以保證原子性,也可以間接保證可見性,因爲他會將私有內存和公共內存中的數據同步。
4.關鍵字volatile解決的是變量在多個線程之間的可見性;而synchronized關鍵字解決的是多個線程之間訪問資源的同步性。