JAVA併發程式設計之Volatile變數
volatile:不穩定的;爆炸性的;反覆無常的
volatile變數是java提供的一種弱同步機制(我覺得只能確保讀取的同步),當把變數宣告為volatile型別後:
1.編譯器與執行時都會注意到這個變數是共享的,會變的,不會將該變數上的操作與其他記憶體操作一起重排序;
2.volatile變數不會被快取在暫存器或者處理器不可見的地方;
因此,讀取volatile型別的變數時總會返回最新值。
package JavaConcurrency; public class TestVolatile { private static volatile boolean value=true; private static class ThreadOne extends Thread{ public void run(){ if(value){ System.out.println("value is :"+value); }else{ System.out.println("value is :"+value); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } private static class ThreadTwo extends Thread{ public void run(){ if(value){ value=false; } if(!value){ value=true; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException{ for (int i = 0; i < 100; i++) { new ThreadTwo().start(); new ThreadOne().start(); } Thread.sleep(500); System.out.println("============GAME OVER==============="); } }
以上程式碼中,ThreadOne 輸出布林型變數value的值,ThreadTwo 改變value的值,使其在false和true之間轉換。
當不用volatile修飾value時,ThreadOne將value的值true快取在ThreadOne的本地記憶體中,每次都從本次記憶體中取值,而不去理會原地址value的值是否發生了變化,因此輸出value的值全為true;使用volatile修飾變數value後,ThreadOne會從原地址對value進行取值,值為false的value會出現在結果中。這就會出現有時候debug版本下程式能正常而生產環境下卻不正常。
檢查某個狀態標記以判斷dosomething(通常為結束迴圈)是volatile的一種典型用法:
volatile boolean asleep; while(!aleep) dosomething();
注意:加鎖機制能確保可見性,也能確保原子性,volatile只能確保可見性。
volatile變數也存在一些侷限性,比如不能確保(value++)操作的原子性,除非我們能確保只有一個執行緒對變數執行寫操作(原子變數實現對變數讀-改-寫的原子操作),當且僅當滿足以下條件時,才應該使用volatile變數:
1.對變數的寫入操作不依賴變數的當前值,或者你能確保單個執行緒更新變數的值;
2.該變數不會與其他狀態變數條件一起納入不變性條件中;
3.在訪問變數時不需要加鎖