volatile 並不能保證併發下 操作的原子性,而是保證了併發時的可見性。
僅在以下場景中可以保證操作的原子性
1 運算的結果不依賴於當前的變量值,或者只有單一線程在修改變量
2 變量不需要其他變量共同參與不變約束
需要的背景知識:
1 java多線程的內存獲取
java對於內存的操作是間接的,每一條線程都有一個工作內存,這些工作內存與主內存之間還要進行 save 和 load操作 才能將數據 存儲進內存 或者 加載進工作內存。
而普通變量與 volatile變量的區別就是 volatile變量可以保證更新過的數據可以優先刷新進內存,並且立即被工作內存獲取到。
2 如何立即獲取到
用volatile修飾的變量在更新數值操作的時候,底層的編譯後字節碼 會在把寫入操作 用內存屏障擴起來,方法就是lock關鍵字。而這樣做的話,在這句賦值操作執行完成之後,其他同時訪問的cpu會進行一次內核無效化——相當於重新執行一次store 和write的操作。
3 爲什麼需要內存屏障
因爲編譯的時候,各條指令需要被重新打亂排序,可以允許cpu不按程序規定的順序將指令發送到各個電路單元去處理,加入內存屏障之後,保證了cpu 只能在執行完這條寫入操作之後,再去執行其他操作。
指令重排序也不是隨機重排序的,也遵循着一定的規則:
控制流次序規則
管程鎖定規則,就是一個鎖的 釋放 必須發生在下一次的 lock之前
volatile變量規則,對於同一個變量的寫操作發生在讀操作之前
線程啓動、終止、中斷規則
對象終結規則
傳遞性,就是 a先於b b先於c a就要先於c
4 爲什麼會重排序
cpu做的優化。。不是死鎖方面的 就是效率方面的 所以重排序主要是優化了效率,一個元件被分配了任務 另一個元件空着 cpu就很不爽,就像流水線分發任務一樣,同一時間點 或者說 在單位時間內,併發的元件數量提升了,整體的效率也就提升了,cpu爽了,電腦爽了,你也爽了