synchronized與volatile
在這之前我們應該先了解一下Java記憶體模型
請戳~~~~>Java記憶體模型
Volatile關鍵字
保證操作變數的可見性和有序性,不保證操作變數的原子性
這段程式碼本應該是20000,但是為什麼比20000小呢??
因為num++這個指令它並不具有原子性,它是分為幾步執行的,volatile能夠保證拿到的時候是正確的資料,但是由於它只走了一步,別的執行緒把這個變數的值改了,它並不知道還是用的之前的資料,所以導致最後的結果比20000小
但是如果用synchronized就會保證原子性
可見性:
A),在彙編層會對volatile修飾的關鍵字加Lock字首
Lock字首指令實際上是一個記憶體屏障,記憶體屏障會提供三個功能
- 它確保指令重排序時不會把其後面的指令排到記憶體屏障之前的位置,也不會把前面的指令排到記憶體屏障的後面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成;
- 它會強制將對快取的修改操作立即寫入記憶體
- 如果是寫操作,它會導致其他CPU中對應的快取行無效
B),在修改變數的過程中
- 將修改變數的副本寫入主記憶體
- 其他執行緒的 副本置為無效
C),讀的時:先判斷volatile關鍵字修飾的變數是否有效,有效直接讀取,反之,則 到主記憶體獲取最新值
有序性(能禁止指令重排序)
1)當程式執行到volatile變數的讀操作或者寫操作時,在其前面的操作的更改肯 定全部已經進行,且結果已經對後面的操作可見;在其後面的操作肯定還沒有進行;
2)在進行指令優化時,不能將在對volatile變數訪問的語句放在其後面執行,也 不能把volatile變數後面的語句放到其前面執行。
synchronized關鍵字
保證原子性 :synchronized關鍵字鎖住的程式碼,一次只能被一個執行緒所操作。
保證可見性:在Java記憶體模型中,synchronized規定,執行緒在加鎖時,先清空工作記憶體,在主存中拷貝最新變數的副本到工作記憶體,執行完程式碼後,將更改後的共享變數的值重新整理到主記憶體中,釋放鎖。
保證有序性:synchronized通過“一個變數 在同一時刻只允許一個執行緒對它進行lock操作”,這條規則決定了持有同一個鎖的兩個同步塊只能序列的併入
區別
1,volatile不會造成執行緒的阻塞,synchronized會。
2,synchronized會 造成執行緒狀態的改變,而執行緒狀態的改變又依賴於作業系統,所以效率會比較低。
3,synchronized可以修飾程式碼塊、方法。volatile只能修飾變數。
4,synchronized能保證原子性、volatile不能。
5,volatile本質是在告訴jvm當前變數在暫存器(工作記憶體)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住。