共享變量在線程間的可見性(多線程程序編寫過程中,目前階段我認(rèn)為最容易出問題的就是共享變量爭奪問題。)
主要明白的知識點一下幾個:
1.synchronized實現(xiàn)可見性
2.Volatile實現(xiàn)可見性
3.指令重排序
4.as if serial 語義
5.volatile使用注意事項
基本定義:
可見性:一個線程對共享變量值的修改,能夠及時被其他線程看到。
共享變量:如果一個變量在多個線程的工作內(nèi)存中都有存在副本,那么這個變量就是這幾個線程的共享變量。
Java內(nèi)存模型(JMM) ?Java memory model 線程共享變量的訪問規(guī)則。
所有變量都存儲于主內(nèi)存中。線程在訪問變量時,一般為在主內(nèi)存中把變量拷貝到自己的工作內(nèi)存中,進(jìn)行操作,不允許直接修改主內(nèi)存的變量,在自己工作內(nèi)存中操作結(jié)束把變量拷貝回去。
每個線程都有自己獨立的工作內(nèi)存,里邊保存該線使用到的變量的副本(主內(nèi)存中該變量的一份拷貝)
兩個規(guī)定:
1.線程對共享變量的所有操作都必須在自己的工作內(nèi)存中進(jìn)行,不能直接從主內(nèi)存中讀寫。
2.不同線程之間無法直接訪問其他線程工作內(nèi)存中的變量,線程間變量值得傳遞需要通過主內(nèi)存。
變更流程中實現(xiàn)共享變量的可見性,必須保證兩點:
1.線程修改后的共享變量值能夠及時從工作內(nèi)存刷新到主內(nèi)存中。
2.其他線程能夠及時把共享變量的最新值從主內(nèi)存更新到自己的工作內(nèi)存中。
Java語言層支持可見性:
Synchronized
1.原子性
2.可見性
JMM關(guān)于Synchronized的規(guī)定:
1.線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存中
2.線程加鎖時,將清空工作內(nèi)存中共享變量的值,從而使用共享變量時需要從主內(nèi)存中重新讀取最新的值(Notice: 加鎖與解鎖需要時同一把鎖)
線程執(zhí)行互斥代碼的過程:
1.獲得互斥鎖。
2.清空工作內(nèi)存。
3.從主內(nèi)存拷貝變量的最新副本到工作內(nèi)存。
4.執(zhí)行代碼
5.將更改后的共享變量的值刷新到主內(nèi)存中
6.釋放互斥鎖
指令重排序
定義:代碼書寫的順序與實際執(zhí)行的順序不同,指令重排序是編譯器或者處理器為了提高程序性能而做的優(yōu)化。
1.編譯器優(yōu)化的重排序(編譯器優(yōu)化)
2.指令集并行重排序(處理器優(yōu)化)
3.內(nèi)存系統(tǒng)的重排序(處理器優(yōu)化)
可能情況舉例:
代碼順序:
int number =1;
int result = 0;
執(zhí)行順序:
int result=0;
int number = 1;
as-if-serial?語義
無論如何重排序,程序執(zhí)行的結(jié)果應(yīng)該與代碼順序執(zhí)行的結(jié)果已知(Java編譯器,運行時和處理器都會保證Java在單線程下遵循as-if-serial語義)
重排序不會給單線程帶來內(nèi)存可見性問題。
多線程中程序交錯執(zhí)行時,重排序可能會造成內(nèi)存可見性問題。
導(dǎo)致共享變量在線程間不可見的原因
1.線程的交叉執(zhí)行。
2.重排序結(jié)合線程交叉執(zhí)行。
3.共享變量更新后的值沒有在工作內(nèi)存與主內(nèi)存之間及時更新。
synchronized 解決方案:
1.保證原子性。
2.避免線程在鎖內(nèi)部交叉執(zhí)行。
3.保證工作內(nèi)存與主內(nèi)存及時更新。
volatile 如何實現(xiàn)內(nèi)存可見性:
通過加入內(nèi)存屏障和禁止重排序優(yōu)化來實現(xiàn)的。
1.對volatile變量執(zhí)行寫操作時,會在寫操作后加入一條store屏障指令。
2.對volatile變量執(zhí)行讀操作時,會在讀操作前加入一條load屏障指令。
也就是說:
volatile變量在每次被線程訪問時,都強迫從主內(nèi)存中重讀該變量的值,而當(dāng)該變量發(fā)生變化時,由強迫線程將最新的值刷新到主內(nèi)存,這樣任何時刻,不同的線程總能看到變量,最新的值。
volatile不能保證volatile變量符合操作的原子性
多線程中安全使用volatile變量,必須同時滿足的條件:
1.對變量的寫入操作不依賴其當(dāng)前值
不滿足舉例:number++ ,count=count*5等
滿足舉例:? boolean變量,記錄溫度變化的變量等。
2.該變量沒有包含在具有其他變量的不變式中
synchronized與volatile比較:
volatile不需要加鎖,比synchronized更輕量級,不會阻塞線程;
從內(nèi)存可見性角度講,volatile讀相當(dāng)于加鎖,volatile寫相當(dāng)已解鎖。
synchronized既可以保證可見性,又可以保證原子性,
volatile 只能保證可見性,不能保證原子性。
總結(jié)
什么是內(nèi)存可見性
Java內(nèi)存模型
實現(xiàn)可見性的方式:synchronized和volatile
final也可以保證內(nèi)存可見性。
synchronized和volatile? 可見性原理
指令重排序
as-if-serial語義
volatile? 可見性,不能原子性。注意事項
可見性在高并發(fā)短時間才會出現(xiàn)。
與JVM和硬件也有關(guān)系。盡可能加保護(hù)。