java多線程之volatile理解

   最近一直在看多線程的一些知識,看了一些書和一些博客,收穫仍是挺多的,最近看了《java併發編程的藝術》這本書感受收穫很大也推薦給各位,同時也結合之前看的博客就好好的總結一下本身所學的東西吧,有不足的地方歡迎各位指正,這篇文章主要是講volatile關鍵字的知識。html

volatile的特性

  • 可見性:volatile在多線程中可以保證共享變量的「可見性」,簡單的說就是當一個線程修改了volatile變量的時候,java線程內存模型可以確保全部的線程看到的這個變量的值是一致的。
  • 防止指令重排序

java內存模型

  • 在學習volatile的知識以前咱們先來簡單瞭解下java內存模型(JMM)引用一張網上很經典的表示java內存模型的圖

  • 大概解釋下這個圖的意思
    • 在多個線程運行的時候每個線程(Thread)都有一個屬於本身的內存空間
    • 多個線程共同使用一個主存
    • 每一個線程在對數據進行修改以前都會先在主存裏面獲取相關數據,而後在本身的工做內存裏面對數據進行操做。

可見性

關鍵的地方就來了,由於每一個線程然都是在本身的內存裏進行操做,然而每一個線程的工做內存之間都是相互不可見的,因此對共享變量的修改並不會立刻被其餘線程看到,因此就會形成多個線程操做同一個數據可是最後結果並非咱們指望的結果。當線程1去首先從主存中加載一個volatile變量到本身的工做內,而後對這個volatile變量進行寫操做,寫入操做結束以後,volatile變量的最新值會立馬刷新到主內存,同時其餘線程中的這個volatile變量會立馬失效,會被強迫從主內存中從新讀取volatile變量的最新值,這就是volatile變量的可見性實現的過程。同時這也能夠看作是一個線程和其餘線程通訊的一個過程。java

防止指令重排序

簡單解釋下指令重排序,重排序指的是編譯器和處理器爲了優化程序的性能會對指令序列進行從新排序的一種手段。在單線程的程序裏,指令的重排序會保證執行結果的正確性,可是在多線程中指令的重排序對程序的執行結果的正確性就得不到保障(指令重排序的一些規則各位能夠去查閱一下,這裏不贅述)。volatile變量防止指令重排序請先看下面的內存屏障介紹。編程

內存屏障

JMM把內存屏障分爲四類(摘自Java併發編程藝術)多線程

屏障類型 指令示例 說明
LoadLoad Barriers Loadl; LoadLoad; Load2 確保Loadl數據的裝載先於Load2及全部後續裝載指令
StoreStore Barriers Storel; StoreStore; Store2 確保Store1數據對其餘處理器可見(刷新到內存)先於Store2及全部後續存儲指令的存儲
LoadStore Barriers Loadl; LoadStore; Store2 確保Loadl數據裝載先於Store2及全部後續的存儲指令刷新到內存
StoreLoad Barriers Storel; StoreLoad; Load2 確保Storel數據對其餘處理器變得可見(指刷新到內存)先於Load2及全部後續裝載指令的裝載。StoreLoad Barriers會使該屏障以前的全部內存訪問指令(存儲和裝載指令)完成以後,才執行該屏障以後的內存訪問指令
  • volatile變量基於保守策略的JMM內存屏障插入策略
    • 在每一個volatile寫操做的前面插入一個StoreStore屏障。
    • 在每一個volatile寫操做的後面插入一個StoreLoad屏障。
    • 在每一個volatile讀操做的後面插入一個LoadLoad屏障。
    • 在每一個volatile讀操做的後面插入一個LoadStore屏障。

看完這個相信各位對volatile防止指令重排序就有一個比較清楚的認識了,解釋一下上面的四條策略,併發

  • 在volatile變量的寫操做前面的其餘寫操做會在volatile變量寫前面執行(提早刷新到主存,對其餘線程可見)
  • volatile變量的寫會比後面的其餘讀寫操做先進行
  • volatile變量讀操做前面的讀操做會在volatile變量讀操做之前進行
  • volatile變量讀操做後面的其餘寫操做會在volatile變量讀操做之後進行

volatile的應用

不少博客中基本上都說了volatile變量通常運用於不依附當前值的操做,好比自增,個人理解是這樣的,若是volatile變量進行依附當前操做的值的運算,那麼就會涉及到讀volatile變量和寫volatile變量這兩個操做,volatile變量的讀操做(從主內存讀取到線程的工做內存)和寫操做(將變量寫到主內存中去)時,這兩個組合起來的操做就是一個非原子性的操做,因此這種狀況下使用volatile關鍵字就不合適,對於基本變量的一些非原子性操做(如自增)能夠考慮使用java.util.concurrent.atomic包下的一些類,或者使用鎖來進行。volatile變量通常用做好比一個標誌變量這種單個讀寫的操做。 附上我的以爲volatile變量應用的一個講得比較好的博客(www.ibm.com/developerwo… 同時各位也能夠去看一下另一篇講解volatile的博文,一樣比較棒(kwsir.cn/2017/10/12/…性能

寫在最後

鑑於本人水平有限,因此若是文章中有不對的地方,十分歡迎各位在評論留言指點,或者發送到本人的qq郵箱549005114@qq.com通知一下本人。謝謝你們。學習

相關文章
相關標籤/搜索