Java並發編程之Java內存模型
1、什麼是Java的內存模型
Java內存模型簡稱JMM(Java Memory Model
),JMM是和多線程並發相關的一組規范。各個jvm實現都要遵循這個JMM規范。才能保證Java代碼在不同虛擬機順利運行。因此,JMM 與處理器、緩存、並發、編譯器有關。它解決瞭CPU 多級緩存、處理器優化、指令重排等導致的結果不可預期的問題。
2、為什麼需要Java內存模型
程序的運行結果依賴於處理器,而不同的處理器規則都不一樣,不同處理器差異是很大的,所以同段代碼在處理器A運行正常,搬到處理器B運行結果是不一樣的,所以為瞭兼容這種差異,推出瞭Java內存模型規范,JMM是一個規范標準,JMM保證瞭不同處理器的處理結果一致,同時也保證不同編譯器、jvm等等的一致性。所以就保證瞭Java語言“書寫一次、到處運行”
3、Java內存模型及操作規范
1.共享變量都是放在主內存中的
2.每個線程都有自己的工作內存,線程隻可操作自己的工作內存
3.線程要操作共享變量,需要從主內存中讀取到工作內存,改變值之後要從工作內存同步到主內存
4、Java內存模型規定的原子操作
Java內存模型的同步交換協議,規定瞭8種原子操作
原子操作:不可被中斷的一個或一系列操作
lock
(鎖定):將主內存中的變量鎖定,為一個線程所獨占unlock
(解鎖):將lock加的鎖解除,其他的線程有機會訪問此變量read
(讀取):作用於主內存變量,將主內存中的變量值讀取到工作內存load
(加載):作用於工作內存,將read讀取到的值保存到工作內存中的變量副本use
(使用):作用於工作內存變量,將值傳遞給線程的代碼執行引擎assign
(賦值):作用於工作內存變量,將執行引擎處理返回的值重新賦值給變量副本store
(存儲):作用於工作內存變量,將變量副本的值傳送到主內存中write
(寫入):作用於主內存變量,將store傳送過來的值寫入到主內存的共享變量中
Java內存模型的同步交互協議,執行上述8種原子操作時必須滿足如下規則
不允許read和load,store和write操作之一單獨出現。即不允許加載或同步工作到一半。
不允許一個線程丟棄它最近的assign操作,即變量在工作內存中改變之後,必須將數據同步回主內存
不允許一個線程無原因地(無assign操作)將數據從工作內存同步到主內存中。
一個新的變量可能在主內存中誕生。
一個變量在同一個時刻隻允許一條線程對其進行lock操作,但lock操作可以被同一條線程重復執行多次,多次lock之後必須要執行相同次數unlock操作,變量才會解鎖
如果對一個對象進行lock操作,那麼會清空工作內存變量中的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始變量的值
如果一個對象事先沒有被lock,就不允許對其進行unlock操作,也不允許去unlock一個被其他線程鎖住的變量。
對一個變量執行unlock操作之前,必須將此變量同步回主內存中(執行store、write)
5、Java內存模型同步協議
Java內存模型的同步協議,操作規范 將一個變量從主內存復制到工作內存要順序執行read、load操作;要將變量從工作內存同步回主內存要用store、write操作。隻要求順序執行,不一定是連續執行
圖引用網上資料:
6、Java內存模型的HB法則
並發編程有三個重要特效:原子行、可見性、有序性
- 原子性:原子性是指一個或者多個操作,要麼全部執行且執行過程不會被其它操作打斷,要麼全部不執行。
- 可見性:可見性是指共享變量對於多個線程都是可見的,也即一個線程修改瞭變量,其它線程馬上就能知道
- 有序性:有序性是指程序的執行順序按照代碼的先後順便執行
在說JMM的happens-before(HB)法則之前,先說說並發編程的有序性。說到並發線程的有序性,還需要涉及到指令重排序
- 什麼是指令重排?
假如我們寫一個程序,我們會期待這些語句的實際執行順便和代碼的順序是一致的,大部分情況是一致的,但實際上,編譯器、JVM 或者 CPU 都有可能出於優化等目的,對執行的順序進行調整,這個就是指令重排序
- 重排序的好處:提高處理速度
代碼順序如圖:
指令重排後,a=100; a= a+100會提到一起執行,效率提高
上面的例子,是可以提高執行效率,但是有時候指令重排是會導致問題的,如下代碼例子,代碼順序是先初始化content,然後設置標識為true,線程B檢測到為true之後,調用content的方法
如果指令重排後,這種情況就會出現沒初始化完成,就直接調用conten的方法
所以,指令重排有好處也有壞處,一般可能是cpu、編譯器或者是內存會進行指令重排,為瞭避免指令重排,保證並發編程的有序性,有時候需要使用synchronized等鎖或者volatile等等方式避免
1.JMM規定瞭happens-before(先行發生)原則,來保證很多操作的有序性。
2.當我們代碼操作不滿足先行發生原則時,則需在編碼時使用volatile、synchronized來保證有序性
JMM的HB法則
- 程序順序規則:每個線程的每個操作都happens-before該線程中任意的後續操作
- 監視器鎖規則:一個鎖的解除,happens-before於隨後對這個鎖的加鎖
- volatile變量規則:對volatile域的寫,happens-before於任意後續對這個volatile域的讀
- 線程啟動規則:在某個線程對象上調用start()方法happens-before被啟動線程中的任意動作
- 線程終止規則:線程中所有操作都先行發生於對此線程的終止檢測,如在線程t1中成功執行瞭t2.join(),則t2中的所有操作對t2可見
- 線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生
- 對象終結規則:一個對象的初始化完成(構造函數執行結束)先行發生於它的finalize方法的開始傳
- 遞性:如果A happens-before於B,且B happens-before 於C,那麼A happens-before於C
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- Java面試必備之JMM高並發編程詳解
- 詳細分析Java內存模型
- Java Volatile關鍵字你真的瞭解嗎
- Java並發之原子性 有序性 可見性及Happen Before原則
- Java內存模型JMM與volatile