1. 程式人生 > >Java 執行緒通訊記憶體模型---主記憶體與工作記憶體

Java 執行緒通訊記憶體模型---主記憶體與工作記憶體

多工和高併發是衡量一臺計算機處理器的能力重要指標之一。一般衡量一個伺服器效能的高低好壞,使用每秒事務處理數(Transactions Per Second,TPS)這個指標比較能說明問題,它代表著一秒內伺服器平均能響應的請求數,而TPS值與程式的併發能力有著非常密切的關係。在討論Java記憶體模型和執行緒之前,先簡單介紹一下硬體的效率與一致性。

硬體的效率與一致性

  由於計算機的儲存裝置與處理器的運算能力之間有幾個數量級的差距,所以現代計算機系統都不得不加入一層讀寫速度儘可能接近處理器運算速度的快取記憶體(cache)來作為記憶體與處理器之間的緩衝:將運算需要使用到的資料複製到快取中,讓運算能快速進行,當運算結束後再從快取同步回記憶體之中沒這樣處理器就無需等待緩慢的記憶體讀寫了。
  基於快取記憶體的儲存互動很好地解決了處理器與記憶體的速度矛盾,但是引入了一個新的問題:快取一致性(Cache Coherence)

。在多處理器系統中,每個處理器都有自己的快取記憶體,而他們又共享同一主存,如下圖所示:多個處理器運算任務都涉及同一塊主存,需要一種協議可以保障資料的一致性,這類協議有MSI、MESI、MOSI及Dragon Protocol等。Java虛擬機器記憶體模型中定義的記憶體訪問操作與硬體的快取訪問操作是具有可比性的,後續將介紹Java記憶體模型。

Java中的記憶體模型

  除此之外,為了使得處理器內部的運算單元能竟可能被充分利用,處理器可能會對輸入程式碼進行亂起執行(Out-Of-Order Execution)優化,處理器會在計算之後將對亂序執行的程式碼進行結果重組,保證結果準確性。與處理器的亂序執行優化類似,Java虛擬機器的即時編譯器中也有類似的指令重排序(Instruction Recorder)優化。

Java中的記憶體模型

Java中的記憶體模型與硬體中的記憶體模型類似.

主記憶體與工作記憶體

  Java記憶體模型的主要目標是定義程式中各個變數的訪問規則,即在虛擬機器中將變數儲存到記憶體和從記憶體中取出變數這樣底層細節。此處的變數與Java程式設計時所說的變數不一樣,指包括了例項欄位、靜態欄位和構成陣列物件的元素,但是不包括區域性變數與方法引數,後者是執行緒私有的,不會被共享。

  Java記憶體模型中規定了所有的變數都儲存在主記憶體中,每條執行緒還有自己的工作記憶體(可以與前面將的處理器的快取記憶體類比),執行緒的工作記憶體中儲存了該執行緒使用到的變數到主記憶體副本拷貝,執行緒對變數的所有操作(讀取、賦值)都必須在工作記憶體中進行,而不能直接讀寫主記憶體中的變數。

不同執行緒之間無法直接訪問對方工作記憶體中的變數,執行緒間變數值的傳遞均需要在主記憶體來完成,執行緒、主記憶體和工作記憶體的互動關係如下圖所示,和上圖很類似。

這裡寫圖片描述

NOTE: 工作記憶體只會拷貝物件的引用,而不是直接拷貝整個物件,物件在堆中生成,對所有物件都是可見的。

主記憶體與工作記憶體之間的通訊

Java執行緒之間的通訊由Java記憶體模型(本文簡稱為JMM)控制,JMM決定一個執行緒對共享變數的寫入何時對另一個執行緒可見。從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(main memory)中,每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。它涵蓋了快取,寫緩衝區,暫存器以及其他的硬體和編譯器優化。
這裡寫圖片描述

記憶體之間的互動操作

  關於主記憶體與工作記憶體之間的具體互動協議,即一個變數如何從主記憶體拷貝到工作記憶體、如何從工作記憶體同步到主記憶體之間的實現細節,Java記憶體模型定義了以下八種操作來完成:

八種執行緒之間的互動指令:

  1. lock(鎖定):作用於主記憶體的變數,把一個變數標識為一條執行緒獨佔狀 。

  2. unlock(解鎖):作用於主記憶體變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他執行緒鎖定。

  3. read(讀取):作用於主記憶體變數,把一個變數值從主記憶體傳輸到執行緒的工作記憶體中,以便隨後的load動作使用

  4. load(載入):作用於工作記憶體的變數,它把read操作從主記憶體中得到的變數值放入工作記憶體的變數副本中。

  5. use(使用):作用於工作記憶體的變數,把工作記憶體中的一個變數值傳遞給執行引擎,每當虛擬機器遇到一個需要使用變數的值的位元組碼指令時將會執行這個操作。

  6. assign(賦值):作用於工作記憶體的變數,它把一個從執行引擎接收到的值賦值給工作記憶體的變數,每當虛擬機器遇到一個給變數賦值的位元組碼指令時執行這個操作。

  7. store(儲存):作用於工作記憶體的變數,把工作記憶體中的一個變數的值傳送到主記憶體中,以便隨後的write的操作。

  8. write(寫入):作用於主記憶體的變數,它把store操作從工作記憶體中一個變數的值傳送到主記憶體的變數中。

JMM對互動指令的約束

如果要把一個變數從主記憶體中複製到工作記憶體,就需要按順尋地執行read和load操作,如果把變數從工作記憶體中同步回主記憶體中,就要按順序地執行store和write操作。

Java記憶體模型只要求上述操作必須按順序執行,而沒有保證必須是連續執行。也就是read和load之間,store和write之間是可以插入其他指令的,如對主記憶體中的變數a、b進行訪問時,可能的順序是read a,read b,load b, load a。Java記憶體模型還規定了在執行上述八種基本操作時,必須滿足如下規則:

  • 不允許read和load、store和write操作之一單獨出現

  • 不允許一個執行緒丟棄它的最近assign的操作,即變數在工作記憶體中改變了之後必須同步到主記憶體中。

  • 不允許一個執行緒無原因地(沒有發生過任何assign操作)把資料從工作記憶體同步回主記憶體中。

  • 一個新的變數只能在主記憶體中誕生,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變數。即就是對一個變數實施use和store操作之前,必須先執行過了assign和load操作。

  • 一個變數在同一時刻只允許一條執行緒對其進行lock操作,lock和unlock必須成對出現

  • 如果對一個變數執行lock操作,將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前需要重新執行load或assign操作初始化變數的值

  • 如果一個變數事先沒有被lock操作鎖定,則不允許對它執行unlock操作;也不允許去unlock一個被其他執行緒鎖定的變數。

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

主記憶體與工作記憶體之間的通訊

兩個執行緒之間進行通訊時,必須經過下面兩個步驟

  • 執行緒A把本地記憶體A中更新過的共享變數重新整理到主記憶體中去。

  • 執行緒B到主記憶體中去讀取執行緒A之前已更新過的共享變數。

這兩個過程就是通過JMM記憶體之間的互動指令實現的。

這裡寫圖片描述

如上圖所示,本地記憶體A和B有主記憶體中共享變數x的副本。

假設初始時,這三個記憶體中的x值都為0。執行緒A在執行時,把更新後的x值(假設值為1)臨時存放在自己的本地記憶體A中。當執行緒A和執行緒B需要通訊時,執行緒A首先會把自己本地記憶體中修改後的x值重新整理到主記憶體中,此時主記憶體中的x值變為了1。

隨後,執行緒B到主記憶體中去讀取執行緒A更新後的x值,此時執行緒B的本地記憶體的x值也變為了1。

從整體來看,這兩個步驟實質上是執行緒A在向執行緒B傳送訊息,而且這個通訊過程必須要經過主記憶體。JMM通過控制主記憶體與每個執行緒的本地記憶體之間的互動,來為java程式設計師提供記憶體可見性保證。

ref: 《深入理解JVM虛擬機器》

相關推薦

Java 執行通訊記憶體模型---記憶體工作記憶體

多工和高併發是衡量一臺計算機處理器的能力重要指標之一。一般衡量一個伺服器效能的高低好壞,使用每秒事務處理數(Transactions Per Second,TPS)這個指標比較能說明問題,它代表著一秒內伺服器平均能響應的請求數,而TPS值與程式的併發能力有

java執行通訊

一個執行緒如果沒有持有物件鎖,將不能呼叫wait(),notify()或者notifyAll()。否則,會丟擲IllegalMonitorStateException異常。 (校注:JVM是這麼實現的,當你呼叫wait時候它首先要檢查下當前執行緒是否是鎖的擁有者

JAVA#執行通訊'札記

public static void main(String[] args) { Counting c=new Counting(); Thread t1=new Thread(c); Thread t2=new Thread(c);

Thinking in Java---執行通訊+三種方式實現生產者消費者問題

前面講過執行緒之間的同步問題;同步問題主要是為了保證對共享資源的併發訪問不會出錯,主要的思想是一次只讓一個執行緒去訪問共享資源,我們是通過加鎖的方法實現。但是有時候我們還需要安排幾個執行緒的執行次序,而在系統內部執行緒的排程是透明的,沒有辦法準確的控制執行緒的切

java 執行通訊的兩種方法

先講如何通訊: 再講 如何 同步。網上的程式碼是有問題的。我註釋了出來。 第一 。\執行緒 共用 一個 稀缺變數: 1、 繼承至 thread 類 public class Innersharethread { public static void main(Stri

無鎖執行通訊(2):例程分析

 首先,我們來看一個例子。   1 //    互動通道“沒有貨”狀態  2 #define COMMUNICATIONCHANNEL_STATE_THINGSNOTEXISTS 0  3 //    互動通道“有貨”狀態  4 #define COMMUNICATIONCHANNEL_STATE_

java執行記憶體模型,執行工作記憶體記憶體

java執行緒記憶體模型 執行緒、工作記憶體、主記憶體三者之間的互動關係圖: key edeas 所有執行緒共享主記憶體 每個執行緒有自己的工作記憶體 refreshing local memory to/from main memory must  co

Java執行Java JVM 記憶體模型總結

Java的併發採用的是共享記憶體模型(而非訊息傳遞模型),執行緒之間共享程式的公共狀態,執行緒之間通過寫-讀記憶體中的公共狀態來隱式進行通訊。多個執行緒之間是不能直接傳遞資料互動的,它們之間的互動只能通過共享變數來實現 同步是顯式進行的。程式設計師必須顯式指定某個方法或某段

執行讀書筆記二(java記憶體模型、volatile變數、記憶體模型synchronized、CAS)

java記憶體模型 java中,執行緒之間的通訊是通過共享記憶體的方式,儲存在堆中的例項域,靜態域以及陣列元素都可以線上程間通訊。java記憶體模型控制一個執行緒對共享變數的改變何時對另一個執行緒可見。 執行緒間的共享變數存在主記憶體中,而對於每一個執行緒,都有一個私有的工

JAVA】從java執行來看java記憶體模型

前言 本來是想寫兩個執行緒,執行緒1輸出1-98的奇數,執行緒2輸出1-98的偶數,交替執行,在測試的時候發現執行緒安全問題,之後又引入到java記憶體模型,下面是幾個demo。   1.版本1 //to print 1 ,3, 5...by thread1, print 2,4,6,

Java執行Java記憶體模型總結

學習資料:http://www.infoq.com/cn/articles/java-memory-model-1 Java的併發採用的是共享記憶體模型(而非訊息傳遞模型),執行緒之間共享程式的公共狀態,執行緒之間通過寫-讀記憶體中的公共狀態來隱式進行通訊。多個執行緒之間

java執行記憶體 java 重排序 java happens-before java 記憶體語義

執行緒之間的通訊機制有兩種:           共享記憶體:在共享記憶體的併發模型裡,執行緒之間共享程式的公共狀態,通過寫-讀記憶體中的公共狀態進行隱式通訊。      

《JVM故障診斷指南》之3 —— Java 執行: JVM持有記憶體的分析

原文連結 原文作者:Byron Kiourtzoglou 翻譯:梅小西(904516706) 前面我們已經討論過JVM裡不同的堆空間,這節我們會給你提供教程,是關於如何從你的活動的應用Java執行緒中確定它持有多少堆空間,以及在哪裡佔用。這裡有個來自Oracle Weblogic 10.0生產

java執行安全、單例模式、JVM記憶體結構等知識學習和整理

知其然,不知其所以然 !在技術的海洋裡,前路漫漫,我一直在迷失著自我。 歡迎訪問我的csdn部落格,我們一同成長! “不管做什麼,只要堅持下去就會看到不一樣!在路上,不卑不亢!” 在下面的題目來自於我要加的一個QQ群,然後要加這個QQ群

記憶體模型執行設計-JMM模型

RoadMap 1 JMM模型     JMM 全稱,Java Memory Model. 這個記憶體模型與Stack,heap GC分代的記憶體模型,不是一回事,兩者是通過不通的維度,將硬體訪問抽象出來的一層抽象的邏輯模型,JVM遮蔽了硬體的直接

Java執行程式設計-(9)-ThreadLocal造成OOM記憶體溢位案例演示原理分析

原文出自 : https://blog.csdn.net/xlgen157387/article/details/78298840 案例程式碼 1、首先看一下程式碼,模擬了一個執行緒數為500的執行緒池,所有執行緒共享一個ThreadLocal變數,每一個執行緒執

System.out,println對多執行的影響,以及記憶體工作記憶體的同步

先說一下之前對System.out.println的誤會先舉個例子package com.yigjn.Thread; public class MyThread extends Thread { private int count = 0; @Override p

執行通訊機制---共享記憶體:訊息傳遞

在併發程式設計中,我們必須考慮的問題時如何在兩個執行緒間進行通訊。這裡的通訊指的是不同的執行緒之間如何交換資訊。 目前有兩種方式: 1、共享記憶體 2、訊息傳遞(actor 模型) 共享記憶體 共享記憶體這種方式比較常見,我們經常會設定一個共享變數。然後多個執行緒去操作

JAVA 執行記憶體可見性

什麼是記憶體可見性 先介紹幾個概念: 共享變數: 如果一個變數在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是這幾個執行緒的共享變數 可見性: 一個執行緒對共享變數值的修改,能夠及時地被其他執行緒看到 JMM( JAVA記憶體模型)

Java執行模型總結

1. 計算機系統 使用快取記憶體來作為記憶體與處理器之間的緩衝,將運算需要用到的資料複製到快取中,讓計算能快速進行;當運算結束後再從快取同步回記憶體之中,這樣處理器就無需等待緩慢的記憶體讀寫了。 快取一致性:多處理器系統中,因為共享同一主記憶體,當多個處理器的運算任務都設計到同一塊記憶體區域