共享變量在線程間的可見性

共享變量在線程間的可見性(多線程程序編寫過程中,目前階段我認(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ù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容