一、共享變量在線程間的可見性安全
二、synchronized實現可見性多線程
三、volatile 實現可見性性能
1)指令重排序優化
2)as-if-serialspa
3)volatile 使用注意事項線程
四、volatile和synchronized的比較對象
一個線程對共享變量值的修改,可以及時地被其餘線程看到。排序
共享變量:若是一個變量在多個線程的工做內存中都存在副本,那麼這個變量就是這幾個線程的共享變量。內存
Java內存模型(JMM):描述了Java程序中各類變量(共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取出變量這樣的底層細節。編譯器
1)全部的變量都存儲在在主內存中。
2)每一個線程都有本身獨立的工做內存,裏面保存該線程使用到的變量副本(主內存中該變量的一份拷貝)。
3)線程對共享變量的全部操做都必須在本身的工做內存中進行,不能直接從主內存中讀寫
4)不一樣線程之間沒法直接訪問其餘線程工做內存中的變量,線程間變量值的傳遞須要經過主內存來完成。
1)首先在本身線程Thread1裏修改共享變量x=1
2)而後更新主內存的x=1
3)其次其餘線程Thread2從主內存中讀取x=1,更新本身線程的值,
這樣連續性的操做,能夠保證任何一個線程的獨立內存中的共享變量都是最新的值!
1)synchronized
2)volatile
3)concurrent 包
1)線程解鎖前,必須把共享變量的最新值刷新到主內存中
2)線程加鎖時,將清空工做內存中共享變量的值,從而使用共享變量時須要從主內存中從新讀取最新的值。(注意:加鎖與解鎖須要同一把鎖)
1)首先得到互斥鎖
2)清空工做內存
3)從主內存拷貝變量的最新副本到工做內存
4)執行代碼
5)將更改後的共享變量值刷新到主內存
6)釋放互斥鎖
代碼書寫的順序與實際執行的順序不一樣,指令重排序是編譯器或處理器爲了提升程序性能而作的優化!
1)編譯器優化
2)指令優化
3)內存系統優化
最後的結果:有可能致使代碼的執行的順序與編寫順序不一致,可是能夠提升CPU性能
不管如何重排序,程序的運行結果都是保持一致的!
單線程中是不能會由於重排序帶來內存可見性的問題。
多線程則會因爲重排序帶來共享變量不一致的問題。
1)線程交叉執行。(原子性來保證)
2)重排序結合線程交叉執行。(原子性來保證)
3)共享變量未及時更新。(內存可見性來保證)
1)擁有原子性
2)擁有內存可見性
3)重量級
因此他可以實現線程間執行操做的安全性!
1)不保證原子性
2)擁有可見性
3)輕量級
1)首先讀取,從主內存中讀取i的值更新到當前工做內存中
2)其次改變,對i進行加1
3)最後更新,從當前工做內存中的值刷新到主內存中去
因此,這不是一個原子操做,在這個操做過程當中勢必會致使線程間交互而致使值的混亂!解決方式就是保證 i++ 具備原子性
1)使用synchronized
2)使用Lock對象,concurrent 包中
Lock lock = new RentrantLock();
try{
lock.lock();
i++
}finally{
lock.unlock();
}
1)對變量的寫入操做不依賴其當前值,好比Boolean值,可是 i++ 或 i=i+5
2)該變量沒有包含在其餘變量的不變式中,好比: low < high (這裏我也不是很清楚)
注意:共享變量都必須是private
final 也實現了內存可見性,由於他的值是不可修改的!
對一個共享變量不只僅要關心他的寫,還關心他的讀,兩者都要加鎖;
volatile是輕量級的,能使用,儘可能使用!