Volatile用法和原理
前言
Volatile的關鍵字從Java5出現後就存在了,很多人做了很長時間的Java開發都未必用的到這個關鍵字,其實它還是很有用的一個關鍵字。
正文
簡單概述
volatile是Java提供的一種輕量級的同步機制,同synchronized相比,volatile更輕量級,在訪問volatile變數時不會執行加鎖操作,因此也就不會使執行的執行緒阻塞。
變數不加volatile
問題1 可見性問題
CPU每次執行指令的時候會對記憶體進行讀寫操作,儘管記憶體的讀寫速度比硬碟的讀寫速度快得多,但是指令的處理速度更快,從記憶體進行讀寫速度相對於指令執行的速度就慢了,所以CPU就會將執行緒用到的變數以及一些複雜的操作複製到快取(暫存器)中處理,然後再將處理的結果寫入記憶體。但是對於多核CPU,執行緒可能執行在不同的CPU快取中,這樣的話就無法保證執行緒的可見性(指執行緒之間的可見性,一個執行緒修改的變數後,另一個執行緒取到變數也是修改後的變數)
//初始化
int i =10;
//每個執行緒都對i++
i++;
對於i來說,執行緒A從記憶體讀取i後存入快取,它取到的值是10,執行緒B也將i從記憶體中讀入快取,取到值也是10。執行緒A對i++,i就變成了11,但是執行緒B中的i還是10,那麼i變數對於執行緒是不可見的了。
問題2 有序性問題
int i = 0;//A步驟
int j = 1;//B步驟
i++;//C步驟
j++;//D步驟
CPU為了保證效能或者在多個執行緒存在的情況下,對於指令的處理順序會有變化(指令重排序:CPU允許將多條指令不按程式規定的順序分別給各個相應的處理器處理),比如它可能先執行了B步驟,然後在執行A步驟,也可能先執行了D步驟,然後再執行C步驟。
變數加volatile
執行緒可見性
//初始化
volatile int i =10;
對於一個變數賦予了volatile關鍵字後那麼變數對所有的執行緒具有可見性,當一個執行緒修改了這個變數的值,編譯器就不會將這個變數複製到快取中,就直接在記憶體中操作,這樣從記憶體中取到的volatile標識的變數就是最新的值,也就對其它執行緒可見了。
執行緒有序性
volatile可以禁止指令重排序,volatile標識的變數,會有記憶體屏障,指令重排序時就不能把後面的指令重排序到記憶體屏障之前的位置,這樣就保證不同的程式碼塊只能序列執行,不能同步執行,這樣就保證了執行緒的有序性。
volatile缺點
volatile不能保證原子性,volatile可以配合synchronized保證原子性。