您的位置:首頁>正文

java volatitle介紹與使用

關於關鍵字volatile可以說是Java虛擬機器提供的羽量級的同步機制, 但是它並不容易完全被正常、完整地理解, 以至於許多程式師都不習慣去使用它, 遇到需要處理多執行緒資料競爭問題的時候一律使用Synchronized來進行同步。 瞭解volatile變數的語義對瞭解多執行緒操作的其他特性很有意義。

當一個變數定義為volatile後, 它將具備兩種特性, 第一是保證此變數對所有執行緒的可見性, 這裡的”可見性“是指當一條執行緒修改了這個變數的值, 新值對於其它執行緒來說是可以立即得知的。 而普通變數是做不到這一點, 普通變數的值在執行緒間傳遞均需要通過主記憶體來完成,

例如, 執行緒A修改一個普通變數的值, 然後向主記憶體進行回寫, 另外一條執行緒B在執行緒A回完成了之後再從主記憶體進行讀取操作, 新變數值才會對執行緒B可見。

特性一:可見性

介紹到這裡可能很多朋友會說, 併發執行緒嗎我把變數聲明稱volatile就可以了嘛, 哪裡有那麼複雜, 事實並非如此, 看段代碼:

import java.util.concurrent.*; /** * by lv xiao long */ public class App { public static volatile int count; public static void increase { ++count; } private static final int THREADS_COUNT = 10; private static CountDownLatch countDownLatch=new CountDownLatch(10); public static void main(String[] args) { // 執行緒池 ExecutorService exec = Executors.newCachedThreadPool; // 啟動10條執行緒, 每條執行緒連續自增1000次。 最後count等於10*1000對嗎? for (int index = 0; index

最後結果你會看到不是10*1000, 按道理來說在執行緒中使用volatile變數, 每次使用之前都要先刷新, 執行引擎看不到不一致情況 , 因此不存在一致性問題, 但是在java裡面運算並非原子操作, 導致volatitle變數的運算在併發下一樣是不安全的。

由於volatitle變數只能保證可見性, 在不符合以下兩條規則的運算場景中, 我們仍然要通過加鎖(使用synchronized或java.util.conrueent中的原子類)來保證原子性。

規則1:運行結果並不依賴變數當前值, 或者能夠確保只有單一執行緒修改變數的值。

規則2:變數不需要與其他的狀態變數共同參與不變約束。

例如下麵場景:

volatile boolean off; public void shutdown{ off=true; } public void doWork{ while(!off){ //do stuff } }

特性二:禁止指令重排序優化

普通的變更僅僅會保證在該方法的執行過程中所有依賴賦值結果的地方都能獲取到正確的結果, 而不能保證變數賦值操作的順序與程式碼中的執行順序一致。 因為在一個執行緒的方法執行過程中無法感知到這點, 這也就是java 記憶體模型中描述的所謂的”執行緒內表現為串列的語義“

Map configOptions; char configText; //此變數必須定義為volatile volatile boolean initialized = false; //假設以下代碼在執行緒A中執行 configOptions=new HashMap; configText=readConfigFile(fileName); processConfigOptions(configText,configOptions); initialized =true; //假設以下代碼在執行緒B中執行 while(!initialized ){ sleep; } doSomethingWithConfig;

上面代碼定義initialized 變數時沒有使用volatile修飾, 就可能會由於指令重排序優化, 導致位於執行緒A中最後一句代碼”initialized =true“提前被執行, 得不到最終你想要的結果。

如果在面試過程中, 遇到有關volatile相關的話題, 可以簡明扼要的說明volatile兩種特性, 以及適用的一些場景, 個人覺得一般都能通過。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示