volatile關鍵字的主要作用是使變量在多個線程間可見。
先看個例子:
先看下賬戶類:
public class Account extends Thread{
private BigDecimal balnace = new BigDecimal("100");
public void run() {
System.out.println("開始消費");
while(balnace.compareTo(BigDecimal.ZERO) > 0) {
}
System.out.println("消費線程停止");
}
public BigDecimal getBalnace() {
return balnace;
}
public void setBalnace(BigDecimal balnace) {
this.balnace = balnace;
}
}
主線程類:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
account.start();
Thread.sleep(3000);
account.setBalnace(BigDecimal.ZERO);
System.out.println("餘額已經被設置爲0");
}
}
看下運行結果:
我們看到當餘額設置爲0的時候,賬戶還是一直被消費,因爲程序一直在運行中,此時有人會說餘額沒有改成功,那麼我們再來驗證下,修改主類如下:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
account.start();
Thread.sleep(3000);
account.setBalnace(BigDecimal.ZERO);
System.out.println("餘額已經被設置爲0");
System.out.println(account.getBalnace());
}
}‘
看下執行結果:
我們看到打印的餘額是0,但是程序還在執行中,那這是爲什麼呢?
我們用前面線程安全與鎖中的一張圖來看下(此處把count緩存balace一樣):
這就是因爲程序爲每個線程分配了獨立的內存空間,每個線程都有自己獨立的變量副本,其實這也是爲了提供程序的性能,每次從主內存中取數據是很耗時的,然後性能提供的同時數據不一致了,這有點CAP的感覺,那有沒有辦法可以解決呢?答案是肯定,要是沒有的話,java也不會這麼被廣泛應用,這個就是volatile關鍵字,我們修改下程序accoun類:
public class Account extends Thread{
private volatile BigDecimal balnace = new BigDecimal("100");
public void run() {
System.out.println("開始消費");
while(balnace.compareTo(BigDecimal.ZERO) > 0) {
}
System.out.println("消費線程停止");
}
public BigDecimal getBalnace() {
return balnace;
}
public void setBalnace(BigDecimal balnace) {
this.balnace = balnace;
}
}
繼續看下執行結果:
開始消費
餘額已經被設置爲0
消費線程停止
0
這時候我們看到消費線程已經停止,這是因爲volatile關鍵字的主要作用是使變量在多線程間可見,如果被volatile修飾,則當變量改變時強制線程執行引擎去主內存中讀取變量,就是多個線程間變量的可見性。