Java併發編程的藝術(二)——Java內存模型

Java內存模型

軟件和硬件的共同目標是在不改變程序執行結果的前提下儘可能提高並行度,從而提高運行的效率(在準確的前提下越快越好)

多線程情況下如何不改變程序執行結果會引發如下的思考:

問題:

  1. 線程之間如何通信
  2. 線程之間如何同步

JVM內存中的方法區以及Java堆是線程共享的區域,在程序運行過程中不斷的有線程修改共享區域中的變量值,那麼如何確保多線程情況下程序運行的結果依舊是正確的?

首先我們需要了解一些基礎的方法論,看看工程師們如何建立穩固的內存模型來支持多線程的正確運行

要想多線程運行準確,需要確保程序運行的順序是正確的,共享的資源也需要按照一定的順序進行讀寫。歸根結底就是對於代碼運行順序的控制與理解。

但是工程師們太貪了,爲了優化真的是不擇手段。有人理直氣壯的說只要能保證咱們的程序最終的執行結果一樣,爲什麼不在編譯器和處理器裏對指令進行重新的排序從而使得指令可以並行運行進而提高效率呢?(典型的所謂只看結果不看過程的理工男)

我不管你中間經歷了什麼,我只要你給我一個正確的結果!!!

於是重排序就誕生了。

1.重排序

重排序:編譯器和處理器對於執行順序的優化,在不影響最終執行結果的前提下對指令進行重新組織排序

重排序確實能帶來一定的優化,主要還是集中在單線程情況下的優化,但是在多線程情況下,隨之而來的就是線程安全問題。比如線程A中的指令可能會用到線程B中的結果,那麼A中的重排序將會影響到程序是否能正常運行。

爲了解決這個問題,工程師們又提出了許多內存模型,通過制定相應的規則來限制編譯器和處理器的重排序操作,確保程序按照正確的語序進行執行。

內存模型基本概念
下圖是JMM內存模型示意圖
-本地內存(虛擬出來的內存,線程自己擁有的)
-主內存(線程共享的)
JMM內存模型示意圖

如何確保內存讀取和程序運行的順序一致?通過增加一些規範,又稱語義

2. Java內存模型中的順序一致性

happens-before
JMM中如果一個操作的執行結果需要對另外一個操作可見,那麼這兩個操作存在happens-before的關係
誤區:並不是前一個操作要在後一個操作之前完成,重點是執行結果的順序

as-if-serial
編譯器和處理器不會對於具有數據依賴的程序進行重排序,因爲該重排序會改變程序的運行結果

順序一致性內存模型
I.一個線程中的所有操作都必須按照程序的順序來執行
II.所有線程都只能看到單一的操作執行順序,每個操作必須原子執行並且立刻對所有線程可見

區別:在JMM中,獲取鎖和釋放鎖之間的臨界區代碼可能存在重排序操作,而順序一致性內存模型則是嚴格遵守執行順序。

多線程領域的正確讀寫問題一直是工程師們頭疼的地方,既然要使用多線程提高效率,必然會增加編程的複雜度,對存儲的控制也提高到了一個新的臺階,但是爲了性能,爲了人類進步的大業,掉點頭髮不算什麼:)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章