Java中volatile關(guān)鍵字的線程的可見性、有序性詳解
引言
在juc多線程并發(fā)編程中,常常需要關(guān)注線程的“可見性”與“有序性”。本文將詳細介紹這兩部分內(nèi)容,以及volatile關(guān)鍵字的使用。
閱讀本文前需要一些jvm運行時內(nèi)存、進程與線程、共享內(nèi)存、鎖等相關(guān)知識。
1、可見性
1.1 定義
當一個對象在多個內(nèi)存中都存在副本時,如果一個內(nèi)存修改了共享變量,其它線程也應(yīng)該能夠看到被修改后的值。
1.2 解釋
解釋這句話,首先我們要知道,進程是操作系統(tǒng)分配資源的最小單位,在每個進程里,都包含有數(shù)據(jù)共享的區(qū)域(例如其中有成員變量、堆、靜態(tài)常量等,詳情查閱jvm運行時內(nèi)存),并可能包含多個線程。 在jvm底層,每個線程想要操作某個共享變量時,不能夠直接操作共享區(qū)域的“主存”,而是要先把數(shù)據(jù)從主存讀到線程自己的高速緩存中,再進行操作,再賦值回去。
“具體來說,當一個線程要讀取某個變量的值時,它首先檢查自己的工作緩存中是否存在該變量的副本。 如果存在,則直接讀取副本的值; 如果不存在,則從主內(nèi)存中獲取該變量的最新值,并將其復(fù)制到自己的工作緩存中。 類似地,當一個線程要修改某個變量的值時,它首先會在自己的工作緩存中修改副本,再根據(jù)一定的策略將變化同步回主內(nèi)存。”
那么可見性的概念相應(yīng)而來:多個線程都存了同一個對象副本,如果此時有一個內(nèi)存修改了共享變量,其他的線程應(yīng)該能夠“看到”,而不是傻b一樣仍舊拿著自己緩存里的值操作了。
1.3 實例說明
例如下面情況,如果main 線程對 run 變量的修改對于 t 線程不可見,導(dǎo)致了 t 線程無法停止:
其原因是t線程頻繁從主內(nèi)存中讀取 run 的值,JIT 編譯器會將 run 的值緩存至自己工作內(nèi)存中的高速緩存中, 減少對主存中 run 的訪問。 那么,當主線程(也就是其他線程)把run變?yōu)閒alse,由于子線程t(當前線程)仍然從高速緩存里讀的run,會認為run并沒有改變,從而邏輯出現(xiàn)問題。
1.4 解決方法:volatile(易變關(guān)鍵字)
作用:volatile可以用來修飾成員變量和靜態(tài)成員變量,他可以避免線程從自己的工作緩存中查找變量的值,必須到主存中獲取它的值。(關(guān)鍵點)
然而volatile并不能保證原子性,例如不加鎖的多線程執(zhí)行函數(shù),對成員變量i進行i++ i--,最后得到的結(jié)果i不一定是不變的。 因為volatile關(guān)鍵字,只能讓該線程在使用數(shù)據(jù)前強制從主存讀取,但讀取后是否發(fā)生改變是看不見的。如果有別的線程在該線程讀取后成功改變了共享內(nèi)存的值,還是會發(fā)生指令交錯。
2、有序性
在多線程環(huán)境下,由于編譯器和處理器對指令進行優(yōu)化和重排序,可能會導(dǎo)致操作的執(zhí)行順序發(fā)生改變,從而違反了代碼編寫的原始順序。
為了保證有序性,可以使用以下方法:
使用volatile關(guān)鍵字:對關(guān)鍵的共享變量使用volatile關(guān)鍵字進行聲明,以確保對該變量的寫操作立即對其他線程可見。
使用synchronized關(guān)鍵字或Lock機制:通過使用同步塊或鎖來保護共享資源的讀寫操作,確保線程之間的有序訪問。
使用原子類:Java提供了一些原子類(如AtomicInteger、AtomicLong等)來進行原子操作,這些原子類的方法能夠保證操作的原子性、可見性和有序性。
簡單來說volatile避免了指令重排,也就避免了多線程中可能產(chǎn)生的問題。
到此這篇關(guān)于Java中volatile關(guān)鍵字的線程的可見性、有序性詳解的文章就介紹到這了,更多相關(guān)volatile的線程的可見性、有序性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Swing實現(xiàn)餐廳點餐系統(tǒng)源碼(收藏版)
這篇文章主要介紹了Java Swing實現(xiàn)餐廳點餐系統(tǒng)源碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02Java調(diào)用opencv IDEA環(huán)境配置的教程詳解
這篇文章主要為大家詳細介紹了Java調(diào)用opencv IDEA環(huán)境配置的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-03-03SpringCloud配置客戶端ConfigClient接入服務(wù)端
這篇文章主要為大家介紹了SpringCloud配置客戶端ConfigClient接入服務(wù)端,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08Java利用JSONPath操作JSON數(shù)據(jù)的技術(shù)指南
JSONPath?是一種強大的工具,用于查詢和操作?JSON?數(shù)據(jù),類似于?SQL?的語法,它為處理復(fù)雜的?JSON?數(shù)據(jù)結(jié)構(gòu)提供了簡單且高效的解決方案,本文將介紹?JSONPath?的基本語法,并通過詳細的?Java?示例展示其實際應(yīng)用,需要的朋友可以參考下2025-04-04代理模式:JAVA靜態(tài)代理和動態(tài)代理的實例和實現(xiàn)詳解
這篇文章主要給大家介紹了關(guān)于Java靜態(tài)代理和動態(tài)代理的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-08-08SpringCloud Alibaba使用Seata處理分布式事務(wù)的技巧
在傳統(tǒng)的單體項目中,我們使用@Transactional注解就能實現(xiàn)基本的ACID事務(wù)了,隨著微服務(wù)架構(gòu)的引入,需要對數(shù)據(jù)庫進行分庫分表,每個服務(wù)擁有自己的數(shù)據(jù)庫,這樣傳統(tǒng)的事務(wù)就不起作用了,那么我們?nèi)绾伪WC多個服務(wù)中數(shù)據(jù)的一致性呢?跟隨小編一起通過本文了解下吧2021-06-06