並發編程之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
,否則並不能保證對象
可見性