天天看點

Java的記憶體模型

 java的後端伺服器開發中"高效并發"是我們經常會碰到的,而要寫出高效的代碼需要更多的積累與實踐。而一些基礎的内容是往這個方向發展的基石。是以我們就來介紹下。

硬體效率的一緻性

 随着硬體技術的發展,處理器的處理能力越來越強大,但是與處理器互動的記憶體的處理能力并沒有提升多少,讀取運算,存儲運算這些IO操作的瓶頸并沒有得以消除,處理器的處理效率比記憶體的處理效率要高好幾個數量級,在這種情況下在處理器和記憶體之間添加了一個高速緩存用以平衡互相關系,也就是将記憶體中的資料先儲存到高速緩存中,處理器處理資料的時候直接從緩存中擷取,處理完成後再從緩存中将資料同步回記憶體中。這樣既不會拖累處理器,也能很好的從記憶體中操作資料。關系圖如下:

Java的記憶體模型

 引入高速緩存解決了處理器和記憶體的沖突,但同時又産生了一個新的問題:緩存一緻性,在多處理器的系統中,每個處理器都有自己的高速緩存,而他們又都共享同一主記憶體。當多個處理器的運算任務都涉及到同一塊主記憶體區域時,将可能導緻各自緩存資料的不一緻。為了解決一緻性問題,需要各個處理器通路緩存時都遵循一些協定。在讀寫時根據協定來操作。這裡講的"記憶體模型"可以了解為在特定操作協定下對特定的記憶體或高速緩存進行讀寫通路的過程抽象。java虛拟機也有自己的記憶體模型,接下來我們看下。

Java記憶體模型

 java的記憶體模型規定所有的變量都存儲在主記憶體中,每條線程都有自己的工作記憶體(類比上面的高速緩存)。線程的工作記憶體中儲存了該線程使用的變量的主記憶體副本拷貝。線程對變量的所有操作(讀取,指派)都必須在工作記憶體中進行。而不能直接讀寫主記憶體中的變量,不同線程之間也無法直接通路對方工作記憶體中的變量。線程間變量的傳遞均需要通過主記憶體來完成。如下圖

Java的記憶體模型

記憶體互動操作

 主記憶體和工作記憶體之間具體的互動協定通過以下8個操作來完成。

Java的記憶體模型

将一個變量從主記憶體中複制到工作記憶體中,需要順序的執行read和load操作。

Java的記憶體模型

如果要将變量從工作記憶體中同步會主記憶體,就要順序的執行store和write操作如下。

Java的記憶體模型

 記憶體模型要求上述兩個操作必須按順序執行,但不是連續,也就是說read和load之間、store和write之間可插入其他指令。可以出現 read a,read b,load a,load b的情況,java記憶體模型還要求了如下的規則,必須準守。

   不允許read和load、store和write操作之一單獨出現,即不允許一個變量從主記憶體讀取了但工作記憶體不接受或者從工作記憶體發起回寫但主記憶體不接受的情況。

   不允許一個線程丢棄它的最近的assign操作。即變量在工作記憶體中改變了之後必須把變化同步會主記憶體。

   不允許一個線程無原因滴(沒有發生任何assign操作)把資料從線程的工作記憶體同步會主記憶體中。

   一個新的變量隻能在主記憶體中“誕生”,不允許在工作記憶體中直接使用一個未被初始化的變量。換句話說就是在對一個變量use、store操作之前必須先執行過了assign和load操作

   一個變量在同一時刻隻允許一條線程對其進行lock操作,但lock操作可以被同一條線程重複執行多次,多次執行lock後,隻要執行相同次數的unlock操作,變量才會被解鎖。

   如果對一個變量執行lock操作,将會清空工作記憶體中此變量的值,在執行引擎是喲歐這個變量之前需要重新執行load或者assign操作初始化變量的值。

   如果一個變量沒有被lock操作鎖定,那麼就不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定住的變量。

   對一個變量執行unlock操作之前,必須先把此變量同步回主記憶體中(store,write操作)

以上八條就是Java記憶體模型的操作規則。

參考《深入了解Java虛拟機》