Java中volatile關(guān)鍵字的線程的可見性、有序性詳解
引言
在juc多線程并發(fā)編程中,常常需要關(guān)注線程的“可見性”與“有序性”。本文將詳細(xì)介紹這兩部分內(nèi)容,以及volatile關(guān)鍵字的使用。
閱讀本文前需要一些jvm運(yùn)行時內(nèi)存、進(jìn)程與線程、共享內(nèi)存、鎖等相關(guān)知識。
1、可見性
1.1 定義
當(dāng)一個對象在多個內(nèi)存中都存在副本時,如果一個內(nèi)存修改了共享變量,其它線程也應(yīng)該能夠看到被修改后的值。
1.2 解釋
解釋這句話,首先我們要知道,進(jìn)程是操作系統(tǒng)分配資源的最小單位,在每個進(jìn)程里,都包含有數(shù)據(jù)共享的區(qū)域(例如其中有成員變量、堆、靜態(tài)常量等,詳情查閱jvm運(yùn)行時內(nèi)存),并可能包含多個線程。 在jvm底層,每個線程想要操作某個共享變量時,不能夠直接操作共享區(qū)域的“主存”,而是要先把數(shù)據(jù)從主存讀到線程自己的高速緩存中,再進(jìn)行操作,再賦值回去。
“具體來說,當(dāng)一個線程要讀取某個變量的值時,它首先檢查自己的工作緩存中是否存在該變量的副本。 如果存在,則直接讀取副本的值; 如果不存在,則從主內(nèi)存中獲取該變量的最新值,并將其復(fù)制到自己的工作緩存中。 類似地,當(dāng)一個線程要修改某個變量的值時,它首先會在自己的工作緩存中修改副本,再根據(jù)一定的策略將變化同步回主內(nèi)存。”

那么可見性的概念相應(yīng)而來:多個線程都存了同一個對象副本,如果此時有一個內(nèi)存修改了共享變量,其他的線程應(yīng)該能夠“看到”,而不是傻b一樣仍舊拿著自己緩存里的值操作了。
1.3 實(shí)例說明
例如下面情況,如果main 線程對 run 變量的修改對于 t 線程不可見,導(dǎo)致了 t 線程無法停止:

其原因是t線程頻繁從主內(nèi)存中讀取 run 的值,JIT 編譯器會將 run 的值緩存至自己工作內(nèi)存中的高速緩存中, 減少對主存中 run 的訪問。 那么,當(dāng)主線程(也就是其他線程)把run變?yōu)閒alse,由于子線程t(當(dāng)前線程)仍然從高速緩存里讀的run,會認(rèn)為run并沒有改變,從而邏輯出現(xiàn)問題。
1.4 解決方法:volatile(易變關(guān)鍵字)
作用:volatile可以用來修飾成員變量和靜態(tài)成員變量,他可以避免線程從自己的工作緩存中查找變量的值,必須到主存中獲取它的值。(關(guān)鍵點(diǎn))
然而volatile并不能保證原子性,例如不加鎖的多線程執(zhí)行函數(shù),對成員變量i進(jìn)行i++ i--,最后得到的結(jié)果i不一定是不變的。 因?yàn)関olatile關(guān)鍵字,只能讓該線程在使用數(shù)據(jù)前強(qiáng)制從主存讀取,但讀取后是否發(fā)生改變是看不見的。如果有別的線程在該線程讀取后成功改變了共享內(nèi)存的值,還是會發(fā)生指令交錯。
2、有序性
在多線程環(huán)境下,由于編譯器和處理器對指令進(jìn)行優(yōu)化和重排序,可能會導(dǎo)致操作的執(zhí)行順序發(fā)生改變,從而違反了代碼編寫的原始順序。
為了保證有序性,可以使用以下方法:
使用volatile關(guān)鍵字:對關(guān)鍵的共享變量使用volatile關(guān)鍵字進(jìn)行聲明,以確保對該變量的寫操作立即對其他線程可見。
使用synchronized關(guān)鍵字或Lock機(jī)制:通過使用同步塊或鎖來保護(hù)共享資源的讀寫操作,確保線程之間的有序訪問。
使用原子類:Java提供了一些原子類(如AtomicInteger、AtomicLong等)來進(jìn)行原子操作,這些原子類的方法能夠保證操作的原子性、可見性和有序性。
簡單來說volatile避免了指令重排,也就避免了多線程中可能產(chǎn)生的問題。
到此這篇關(guān)于Java中volatile關(guān)鍵字的線程的可見性、有序性詳解的文章就介紹到這了,更多相關(guān)volatile的線程的可見性、有序性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis實(shí)現(xiàn)批量修改-xml方式
這篇文章主要介紹了mybatis實(shí)現(xiàn)批量修改-xml方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Java實(shí)現(xiàn)的時間戳與date對象相互轉(zhuǎn)換功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)的時間戳與date對象相互轉(zhuǎn)換功能,結(jié)合具體實(shí)例形式分析了java日期與時間戳類型的表示與轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-06-06

