併發安全的特性 -- 可見性與有序性(happens-before)

導致共享變量在線程之間不可見的原因:

  線程交叉執行
  重排序結合線程交叉執行
  共享變量更新後沒有即時在工作內存和主存中即時更新

解決可見性問題

  一、可見性—synchronize

JMM關於synchronized的兩條規定:

  1.線程解鎖前,必須把共享變量的最新值刷新到主內存。
  2.線程加鎖前,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(加鎖和解鎖是同一把鎖

  二、可見性—volatile

通過加入內存屏障和禁止重排序優化來實現

  1.對volatile變量進行寫操作時,會在寫操作後加入一條store屏障指令,將本地內存中的共享變量值刷新到主存中。
  2.對volatile變量進行讀操作時,會在讀操作前加入一條load屏障指令,從主存中讀取共享變量。
在這裏插入圖片描述
  一般來說,使用volatile有兩個條件,一是對變量的寫操作不依賴於當前值,第二是該變量沒有包含在具有其他變量不變的式子中。因此volatile比較適合作爲狀態標記量,用來保證初始化一定已經完成。
在這裏插入圖片描述

有序性:happens-before原則

  Java內存模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程的程序執行,卻會影響到多線程併發執行的正確性。

  可以通過volatile、synchronize、Lock來保證一定的有序性。

JMM中具有一定先天的有序性,不需要任何手段就能保證有序性,這就是我們經常見到的happens-before原則。重排序需要遵守happens-before規則,不能說你想怎麼排就怎麼排。

  happens-before規則主要有以下幾條規則:

1.程序次序規則: 一個線程內,按照代碼順序,書寫在前面的操作先行發生與書寫在後面的操作。(但是也只能是單線程的時候纔是安全的)
保證單線程的執行結果是正確的,對於無關緊要的重排序是允許的。
2.鎖定規則: 一個unLock操作先行發生於後面對同一個鎖的lock操作。
3.volatile變量規則: 對一個變量的寫操作先行發生與後面對這個變量的讀操作。
4.傳遞規則: 如果操作A先行發生於操作B,而操作B又先發生於操作C,則可以得出操作A先行發生於操作C。

  前四條規則是比較重要的,後四條是比較顯而易見的。

1.線程啓動規則: Thread對象的start()方法先行發生於此線程的每一個動作
2.線程中斷規則: 對線程interrupt()方法的調用先行發生與被中斷線程的代碼檢測到中斷事件的發生。
3.線程終結規則: 線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行。
4.對象終結規則: 一個對象的初始化完成先行發生於他的finalize()方法的開始。

  如果兩個操作的執行次序無法從happens-before規則推導出來,那麼就不能保證他們的有序性,虛擬機就可以隨意的對他們進行重排序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章