詳解java如何正確使用volatile
volatile關(guān)鍵字在java多線程中有著比較重要作用,volatile主要作用是可以保持變量在多線程中是實(shí)時可見的,是java中提供的最輕量的同步機(jī)制。
可見性
在Java的內(nèi)存模型中所有的的變量(這里的變量是類全局變量,并不是局部變量,局部變量在方法內(nèi)并沒有線程安全的問題,因?yàn)樽兞侩S方法調(diào)用完成而銷毀)都是存放在主內(nèi)存中的,而每個線程有自己的工作內(nèi)存,每次線程執(zhí)行時,會從主內(nèi)存獲取變量的拷貝,對變量的操作都在線程的工作內(nèi)存中進(jìn)行,不同線程之間也不能共享工作內(nèi)存,只能從主內(nèi)存讀取變量的拷貝。具體可以通過下圖來表示:

然而對于volatile(使用synchronized/final修飾都具有可見性)來說打破了上述的規(guī)則,即當(dāng)線程修改了變量的值,其他線程可以立即知道該變量的改變。然而對于普通變量來說,當(dāng)一個線程修改了變量,需要先將變量寫回主內(nèi)存,其他線程從主內(nèi)存讀取變量后才對該線程可見。似乎從以上的描述可以推導(dǎo)出只要使用volatile修飾的變量就可以保證該變量在多線程環(huán)境下操作是安全的,因?yàn)樗鼘τ谒芯€程的工作內(nèi)存都是可見的也就是說一致的。這么理解確實(shí)沒錯,但是在java中很多運(yùn)算都不是原子的,所以在java的一些運(yùn)算中使用volatile并不能保證線程安全問題。讓我們來看一個例子:
public class test{
private static volatile t=0;
private static int add(){
return t++;
}
public static void testVolatile(){
for (int i=0;i<20;i++){
Thread thread=new Thread(()-> {
for (int j=0;j<1000;j++) {
add();
}
});
thread.start();
}
while (Thread.activeCount()>1){
Thread.yield();
}
System.out.println(t);
}
public static void main(String[] args){
testVolatile();
}
}
預(yù)期這個t值應(yīng)該是20000,但是會出現(xiàn)t值小于20000的情況,原因大家應(yīng)該猜到了,問題出在t++上,t++并不是一個原子操作,t++的操作在java中代表先獲取t值,再加1,再賦值還t。在獲取t值時因?yàn)槭莢olatile修飾的,所以可以獲取線程最新值,然而在加1的時候就不能保證了,有可能其他線程已經(jīng)加1了。
那么什么場景使用volatile是最合適的呢?
* 在變量運(yùn)算不依賴當(dāng)前值
* 變量不需要與其他狀態(tài)變量共同參與不變約束
翻譯成中文就是對于那些在多線程中既有讀又有寫的變量,完全可以使用volatile修飾,這樣就對于讀操作就不要使用lock/synchronized比較重的操作了,直接讀就是,因?yàn)樽兞渴强梢姷摹?/p>
相關(guān)文章
mybatis-plus查詢無數(shù)據(jù)問題及解決
這篇文章主要介紹了mybatis-plus查詢無數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
深入了解Java中循環(huán)結(jié)構(gòu)的使用
Java中有三種主要的循環(huán)結(jié)構(gòu):while 循環(huán)、do…while 循環(huán)和for 循環(huán)。本文將來和大家一起講講Java中這三個循環(huán)的使用,需要的可以參考一下2022-08-08
springmvc+kindeditor文件上傳實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了springmvc+kindeditor文件上傳實(shí)例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08
Springboot上傳excel并將表格數(shù)據(jù)導(dǎo)入或更新mySql數(shù)據(jù)庫的過程
這篇文章主要介紹了Springboot上傳excel并將表格數(shù)據(jù)導(dǎo)入或更新mySql數(shù)據(jù)庫的過程 ,本文以Controller開始,從導(dǎo)入過程開始講述,其中包括字典表的轉(zhuǎn)換,需要的朋友可以參考下2018-04-04
SpringBoot異步任務(wù)實(shí)現(xiàn)下單校驗(yàn)庫存的項(xiàng)目實(shí)踐
在開發(fā)中,異步任務(wù)應(yīng)用的場景非常的廣泛,本文主要介紹了SpringBoot異步任務(wù)實(shí)現(xiàn)下單校驗(yàn)庫存的項(xiàng)目實(shí)踐,具有一定的參考價值,感興趣的可以了解一下2023-09-09
SpringBoot 如何實(shí)時刷新靜態(tài)文件
這篇文章主要介紹了SpringBoot如何實(shí)時刷新靜態(tài)文件,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
java并發(fā)編程JUC CountDownLatch線程同步
這篇文章主要介紹CountDownLatch是什么、CountDownLatch 如何工作、CountDownLatch 的代碼例子來展開對java并發(fā)編程JUC CountDownLatch線程同步,需要的朋友可以參考下面文章內(nèi)容2021-09-09

