Java并發(fā)編程之Java內(nèi)存模型
1、什么是Java的內(nèi)存模型
Java內(nèi)存模型簡稱JMM(Java Memory Model
),JMM是和多線程并發(fā)相關(guān)的一組規(guī)范。各個jvm實現(xiàn)都要遵循這個JMM規(guī)范。才能保證Java代碼在不同虛擬機順利運行。因此,JMM 與處理器、緩存、并發(fā)、編譯器有關(guān)。它解決了CPU 多級緩存、處理器優(yōu)化、指令重排等導致的結(jié)果不可預期的問題。
2、為什么需要Java內(nèi)存模型
程序的運行結(jié)果依賴于處理器,而不同的處理器規(guī)則都不一樣,不同處理器差異是很大的,所以同段代碼在處理器A運行正常,搬到處理器B運行結(jié)果是不一樣的,所以為了兼容這種差異,推出了Java內(nèi)存模型規(guī)范,JMM是一個規(guī)范標準,JMM保證了不同處理器的處理結(jié)果一致,同時也保證不同編譯器、jvm等等的一致性。所以就保證了Java語言“書寫一次、到處運行”
3、Java內(nèi)存模型及操作規(guī)范
1.共享變量都是放在主內(nèi)存中的
2.每個線程都有自己的工作內(nèi)存,線程只可操作自己的工作內(nèi)存
3.線程要操作共享變量,需要從主內(nèi)存中讀取到工作內(nèi)存,改變值之后要從工作內(nèi)存同步到主內(nèi)存
4、Java內(nèi)存模型規(guī)定的原子操作
Java內(nèi)存模型的同步交換協(xié)議,規(guī)定了8種原子操作
原子操作:不可被中斷的一個或一系列操作
lock
(鎖定):將主內(nèi)存中的變量鎖定,為一個線程所獨占unlock
(解鎖):將lock加的鎖解除,其他的線程有機會訪問此變量read
(讀?。鹤饔糜谥鲀?nèi)存變量,將主內(nèi)存中的變量值讀取到工作內(nèi)存load
(加載):作用于工作內(nèi)存,將read讀取到的值保存到工作內(nèi)存中的變量副本use
(使用):作用于工作內(nèi)存變量,將值傳遞給線程的代碼執(zhí)行引擎assign
(賦值):作用于工作內(nèi)存變量,將執(zhí)行引擎處理返回的值重新賦值給變量副本store
(存儲):作用于工作內(nèi)存變量,將變量副本的值傳送到主內(nèi)存中write
(寫入):作用于主內(nèi)存變量,將store傳送過來的值寫入到主內(nèi)存的共享變量中
Java內(nèi)存模型的同步交互協(xié)議,執(zhí)行上述8種原子操作時必須滿足如下規(guī)則
不允許read和load,store和write操作之一單獨出現(xiàn)。即不允許加載或同步工作到一半。
不允許一個線程丟棄它最近的assign操作,即變量在工作內(nèi)存中改變之后,必須將數(shù)據(jù)同步回主內(nèi)存
不允許一個線程無原因地(無assign操作)將數(shù)據(jù)從工作內(nèi)存同步到主內(nèi)存中。
一個新的變量可能在主內(nèi)存中誕生。
一個變量在同一個時刻只允許一條線程對其進行l(wèi)ock操作,但lock操作可以被同一條線程重復執(zhí)行多次,多次lock之后必須要執(zhí)行相同次數(shù)unlock操作,變量才會解鎖
如果對一個對象進行l(wèi)ock操作,那么會清空工作內(nèi)存變量中的值,在執(zhí)行引擎使用這個變量前,需要重新執(zhí)行l(wèi)oad或assign操作初始變量的值
如果一個對象事先沒有被lock,就不允許對其進行unlock操作,也不允許去unlock一個被其他線程鎖住的變量。
對一個變量執(zhí)行unlock操作之前,必須將此變量同步回主內(nèi)存中(執(zhí)行store、write)
5、Java內(nèi)存模型同步協(xié)議
Java內(nèi)存模型的同步協(xié)議,操作規(guī)范 將一個變量從主內(nèi)存復制到工作內(nèi)存要順序執(zhí)行read、load操作;要將變量從工作內(nèi)存同步回主內(nèi)存要用store、write操作。只要求順序執(zhí)行,不一定是連續(xù)執(zhí)行
圖引用網(wǎng)上資料:
6、Java內(nèi)存模型的HB法則
并發(fā)編程有三個重要特效:原子行、可見性、有序性
- 原子性:原子性是指一個或者多個操作,要么全部執(zhí)行且執(zhí)行過程不會被其它操作打斷,要么全部不執(zhí)行。
- 可見性:可見性是指共享變量對于多個線程都是可見的,也即一個線程修改了變量,其它線程馬上就能知道
- 有序性:有序性是指程序的執(zhí)行順序按照代碼的先后順便執(zhí)行
在說JMM的happens-before(HB)法則之前,先說說并發(fā)編程的有序性。說到并發(fā)線程的有序性,還需要涉及到指令重排序
- 什么是指令重排?
假如我們寫一個程序,我們會期待這些語句的實際執(zhí)行順便和代碼的順序是一致的,大部分情況是一致的,但實際上,編譯器、JVM 或者 CPU 都有可能出于優(yōu)化等目的,對執(zhí)行的順序進行調(diào)整,這個就是指令重排序
- 重排序的好處:提高處理速度
代碼順序如圖:
指令重排后,a=100; a= a+100會提到一起執(zhí)行,效率提高
上面的例子,是可以提高執(zhí)行效率,但是有時候指令重排是會導致問題的,如下代碼例子,代碼順序是先初始化content,然后設(shè)置標識為true,線程B檢測到為true之后,調(diào)用content的方法
如果指令重排后,這種情況就會出現(xiàn)沒初始化完成,就直接調(diào)用conten的方法
所以,指令重排有好處也有壞處,一般可能是cpu、編譯器或者是內(nèi)存會進行指令重排,為了避免指令重排,保證并發(fā)編程的有序性,有時候需要使用synchronized等鎖或者volatile等等方式避免
1.JMM規(guī)定了happens-before(先行發(fā)生)原則,來保證很多操作的有序性。
2.當我們代碼操作不滿足先行發(fā)生原則時,則需在編碼時使用volatile、synchronized來保證有序性
JMM的HB法則
- 程序順序規(guī)則:每個線程的每個操作都happens-before該線程中任意的后續(xù)操作
- 監(jiān)視器鎖規(guī)則:一個鎖的解除,happens-before于隨后對這個鎖的加鎖
- volatile變量規(guī)則:對volatile域的寫,happens-before于任意后續(xù)對這個volatile域的讀
- 線程啟動規(guī)則:在某個線程對象上調(diào)用start()方法happens-before被啟動線程中的任意動作
- 線程終止規(guī)則:線程中所有操作都先行發(fā)生于對此線程的終止檢測,如在線程t1中成功執(zhí)行了t2.join(),則t2中的所有操作對t2可見
- 線程中斷規(guī)則:對線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生
- 對象終結(jié)規(guī)則:一個對象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行發(fā)生于它的finalize方法的開始傳
- 遞性:如果A happens-before于B,且B happens-before 于C,那么A happens-before于C
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
類似Object監(jiān)視器方法的Condition接口(詳解)
下面小編就為大家?guī)硪黄愃芆bject監(jiān)視器方法的Condition接口(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05關(guān)于遠程調(diào)用RestTemplate的使用避坑指南
這篇文章主要介紹了關(guān)于遠程調(diào)用RestTemplate的使用避坑指南,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10SpringBoot實現(xiàn)列表數(shù)據(jù)導出為Excel文件
這篇文章主要為大家詳細介紹了在Spring?Boot框架中如何將列表數(shù)據(jù)導出為Excel文件,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下2024-02-02Springboot通過lucene實現(xiàn)全文檢索詳解流程
Lucene是一個基于Java的全文信息檢索工具包,它不是一個完整的搜索應(yīng)用程序,而是為你的應(yīng)用程序提供索引和搜索功能。Lucene 目前是 Apache Jakarta 家族中的一個開源項目,也是目前最為流行的基于 Java 開源全文檢索工具包2022-06-06javaSE,javaEE,javaME的區(qū)別小結(jié)
本篇文章小編就為大家簡單說說JavaSE、JavaEE、JavaME三者之間的區(qū)別,需要的朋友可以過來參考下,感興趣的小伙伴們可以參考一下2023-08-08json-lib將json格式的字符串,轉(zhuǎn)化為java對象的實例
下面小編就為大家?guī)硪黄猨son-lib將json格式的字符串,轉(zhuǎn)化為java對象的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03apollo與springboot集成實現(xiàn)動態(tài)刷新配置的教程詳解
這篇文章主要介紹了apollo與springboot集成實現(xiàn)動態(tài)刷新配置,本文分步驟給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06