閱讀本文約「3分鐘」segmentfault
上一次咱們說到synchronized互斥代碼的實現過程,若是有忘記或不清楚的能夠去上篇看看。
【Java貓說】Java多線程以內存可見性(上篇)安全
今天咱們瞭解下重排序。多線程
其使代碼書寫的順序與實現執行的順序不一樣,指令重排序是編譯器或處理器爲了提升程序性能而作的優化,能夠分爲
一、編譯器優化的重排序(編譯器優化)
二、指令級並行重排序(處理器優化)
三、內存系統的重排序(處理器優化)性能
而as-if-serial語義原則是指:不管如何重排序,程序執行的結果應該與代碼順序執行的結果一致(Java編譯器、運行時和處理器都會保證Java在單線程下遵循as-if-serial語義)優化
int num1 = 1; int num2 = 2; int sum = num1 + num2;
對於執行的單線程而言,第一、2行的順序能夠重排,但第3行不能
由此,重排序並不會給單線程帶來內存可見性問題線程
可是在多線程中程序交錯執行時,重排序可能會形成內存可見性問題code
這裏羅列了幾個緣由,致使共享變量在線程間不可見的緣由:
一、線程的交叉執行(synchronized原子性)
二、重排序結合線程交叉執行(synchronized原子性)
三、共享變量更新後的值沒有在工做內存與主內存間及時更新(synchronized可見性)對象
而對於另外一個對象volatile而言其實現了可見性,可是不能保證原子性(不能保證volatile變量符合操做是的原子性)排序
深刻來講,是經過加入內存屏障和禁止重排序優化來實現的。
一、對volatile變量執行寫操做時,會在寫操做後加一條store屏障指令
二、對volatile變量執行讀操做時,會在讀操做前加入一條load屏障指令內存
由此咱們能夠分爲讀寫volatile變量的兩種操做。
線程寫volatile變量的過程:
一、改變線程工做內存中volatile變量副本的值
二、將改變後的副本的值從工做內存刷新到主內存
線程讀volatile變量的過程:
一、從主內存中讀取volatile變量的最新值到線程的工做內存中
二、從工做內存中讀取volatile變量的副本
注意:volatile是不能保證原子性的
想要在多線程中安全的使用volatile變量,必須同時知足一下幾個條件:
一、對變量的寫入操做不依賴其當前值
- 不知足:number++、count = count * 5 - 知足:boolean變量、記錄數據變化的變量等
二、該變量沒有包含在具備其餘變量的不變式中
- 不知足:不變式 low < up
最後咱們來比較下這兩個對象吧
- volatile不須要加鎖,比synchronized更輕量級,不會阻塞線程 - 從內存可見性角度講,volatile讀至關於加鎖,volatile寫至關於解鎖 - synchronized即能保證可見性,又能保證原子性,而volatile只能保證可見性,沒法保證原子性 - volatile沒有synchronized使用的普遍
本文已轉載我的技術公衆號:UncleCatMySelf
歡迎留言討論與點贊
上一篇推薦:【Java貓說】Java多線程以內存可見性(上篇)
下一篇推薦:【Java貓說】Java對象的行爲