並發編程之volatile
- 2019 年 12 月 31 日
- 筆記
文章目錄
並發編程之volatile
- volatile稱之為輕量級鎖,保證了可見性和原子性。
- volatile不會引起上下文切換,因此是輕量級的
volatile的作用
- 保障可見性,有序性和
Long,double類型變數讀寫操作的原子性 volatile僅僅能保證對其修飾的變數的寫操作以及讀操作本身的原子性,而這並不表示volatile變數的賦值操作一定具有原子性,例如,如下對volatile修飾的變數count的賦值操作並不是原子操作:count++count++可以分為如下步驟- 讀取count的值
count+1- 將
count+1的值賦值給count
- 如果
count是一個共享變數,那個該賦值操作實際上是一個read-modify-write操作。其執行過程中其他執行緒可能已經更新了count的值,因此該操作不具備不可分割性,也就不是原子操作。如果變數count是一個局部變數,那麼該賦值操作就是一個原子操作。
- 一般而言如果對volatile修飾的賦值操作,其右邊的表達式中只要設計共享變數(包括被volatile修飾的變數本身),那麼這個賦值操作就不是原子操作,此時就需要結合
鎖來保證原子性了
原理
保證可見性原理
- 對volatile修飾的變數的讀操作之前插入一個
載入屏障,能夠刷新處理器快取,使其讀取的讀取到的變數都是執行緒更新後的最新值。 - 對volatile修飾的變數的寫操作(修改)之後插入一個
存儲屏障,能夠沖刷處理器快取,保證後續的執行緒讀取到的值是最新的。
保證有序性原理
- 結合釋放屏障和獲取屏障保證了有序性
總結
- volatile的寫操作相當於鎖的釋放的效果,java虛擬機會在該操作之前插入一個釋放屏障,並在該操作只有插入一個存儲屏障
- 釋放屏障禁止了volatile寫操作與該操作之前的任何讀寫操作進行重排序,從而保證了volatile寫操作之前的任何讀寫操作會先於volatile寫操作之前提交,即其他執行緒看到寫執行緒對volatile變數的更新時,寫執行緒在更新volatile變數之前執行的記憶體操作的結果對於度鮮橙必然是可見的。即是保障了有序性
- 存儲屏障保證了在寫操作之後的更新能夠沖刷處理器快取,使得後續的讀執行緒能夠獲取最新的值
- volatile的讀操作相當於獲取鎖的效果,Java虛擬機會在該操作之前插入一個載入屏障,並在該操作之後插入一個獲取屏障
- 載入屏障用於刷新處理器快取區,保證讀取到volatile修飾變數的最新值,保證可見性
- 獲取屏障禁止volatile讀操作之後的任何讀寫操作與volatile讀操作進行重排序。因此保證了有序性
volatile變數的開銷
- volatile變數的讀寫不會導致上下文切換,因此開銷比鎖小
- 讀取volatile變數每次都需要從高速快取或者主記憶體中讀取,而無法暫存在暫存器中,因此可能比讀取普通變數的成本要高
使用場景
- 使用volatile變數作為狀態標誌。比如
volatile int flag=false,其他執行緒會讀取該狀態作為執行某一個操作的依據 - 單例模式下的雙重校驗鎖的實現效果,其中必須使用
volatile,否則並不能保證對象可見性

