volatile vs synchronized

今天來聊一聊Java併發編程中兩個經常使用的關鍵字:volatile和synchronized。在介紹這兩個關鍵字以前,首先要搞明白併發編程中的兩個問題:java

  1. 線程之間是如何通訊的程序員

  2. 線程之間如何同步編程


Java內存模型

Java線程的通訊由Java內存模型(JMM)控制,Java內存模型的抽象如圖:微信

Java線程之間的通訊老是隱式進行,通訊過程對程序員徹底透明。多個線程經過讀-寫共享內存來實現通訊。網絡

圖中線程A與線程B通訊的具體步驟是:多線程

  1. 線程A把更新過的共享變量刷新到主內存中併發

  2. 線程B從主內存讀取共享變量ide

例如,共享變量x的初始值爲0,線程A將x修改成1(x=x+1),線程B讀取到的x就是1,對於程序員來說,就是線程A給線程B發消息說它把x的值更新爲1。性能



第一個問題搞明白了,再思考一下第二個問題。線程之間如何同步?在併發編程中,有三個重要的概念:原子性、可見性、一致性。atom

原子性

在Java中,對基本數據類型的讀取和賦值操做都屬於原子操做。

x = 10;
x = x + 1;

上面兩條語句中,第一句是原子操做,而第二句不是,爲何呢?實際上,第二句代碼被編譯爲3條指令:

  • 從內存中取x的值

  • x+1操做

  • 計算結果存入內存


可見性

當多個線程訪問同一變量時,若是有一個線程修改了這個變量,那麼其餘線程馬上能夠看到修改後的值。


有序性

CPU執行指令是按照前後順序執行的,可是指令的順序並不必定等同於代碼的順序,編譯器編譯過程當中,爲了提升性能,經常進行指令重排序。這種重排序不會改變單線程的語義,也就是說,你寫的一段代碼若是是單線程執行,編譯器可能對執行進行重排序,但不論如何排序,最後獲得的結果都是相同的。


另外,若是存在數據依賴性,編譯器不會改變依賴關係的執行順序。數據依賴性是指兩個操做訪問同一個變量,其中一個是寫操做,那麼這兩個操做就有數據依賴性。


重排序對應多線程有哪些影響呢,咱們經過一段代碼來看一下:

class ReorderExample {
   int a = 0;
   boolean flag = false;
   public void writer() {
       a = 1; // 1
       flag = true; // 2
   }
   Public void reader() {
       if (flag) { // 3
       int i = a * a; // 4
       ……
       }
   }
}

上述代碼中,flag是變量a被初始化的標識,若是此時有兩個線程A和B,A執行writer()方法,B執行reader()方法。因爲1和二、3和4不存在數據依賴性,那麼就有可能出現這種狀況:

  • A先執行語句2

  • B執行了語句3和4

  • A執行語句1

最終的結果並非咱們想要的,此時,重排序破壞了語義。


線程同步

對於上面所說的線程同步問題如何避免呢?可使用Java中的volatile和synchronized這兩個關鍵字。


volatile

volatile關鍵字比較輕量級,只能夠修飾變量。volatile修飾的變量,若是值被更新,會當即刷新主內存,而讀volatile修飾的變量時,JMM會把線程對應的本地內存置爲無效,從主內存中讀取。這樣volatile就能夠保證線程的可見性。


volatile關鍵字在必定程度上能夠保證有序性:

  • 當第二個操做是volatile寫時,不能進行重排序

  • 當第一個操做是volatile讀時,不能進行重排序

  • 當第一個操做是volatile寫,第二個操做是volatile讀時,不能重排序

爲了實現這些語義,JMM採用屏障插入策略:

  • 在volatile寫操做前插入StoreStore屏障,後面插入StoreLoad屏障

  • 在volatile讀操做後面插入LoadLoad屏障和LoadStore屏障

也就是說,volatile寫操做前的全部寫操做都必須執行完,且須要等到volatile寫操做執行後才能執行讀操做。volatile讀操做執行完以後才能夠進行其餘操做。

能夠把volatile當作一個屏障,其前面的操做不能放到volatile操做後面,後面的操做也不能放到volatile操做前面。


synchronized

synchronized比較重量級,能夠用來修飾方法。synchronized關鍵字是給修飾對象加鎖,只有得到鎖的線程才能夠執行,執行完後釋放鎖。所以synchronized保證了原子性和可見性。



文中圖片來源於網絡


本文分享自微信公衆號 - 代碼潔癖患者(Jackeyzhe2018)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索