Java的多線程以及內存模型的知識點梳理,有想到過這些嗎?

  • 時間:2018-12-31 23:06 作者:墨雨軒夏 來源:墨雨軒夏 閱讀:143
  • 掃一掃,手機訪問
摘要:JMM大致形容:JMM形容了線程如何與內存進行交互。Java虛擬機規范視圖定義一種Java內存模型,來屏蔽掉各種操作系統內存訪問的差異,以實現Java程序在各種平臺下都能達到一致的訪問效果。JMM形容了JVM如何與計算機的內存進行交互JMM都是圍繞著原子性,有序性和可見性進行開展的JMM的主要目標是

JMM大致形容:

JMM形容了線程如何與內存進行交互。Java虛擬機規范視圖定義一種Java內存模型,來屏蔽掉各種操作系統內存訪問的差異,以實現Java程序在各種平臺下都能達到一致的訪問效果。

JMM形容了JVM如何與計算機的內存進行交互

JMM都是圍繞著原子性,有序性和可見性進行開展的

JMM的主要目標是定義程序中各個變量的訪問規則,虛擬機將變量存儲到內存和從內存取出變量這樣的底層細節。此處的變量指在堆中存儲的元素。

多線程的時候為什么容易出錯?

Java內存模型規定所有的共享變量都存儲在主內存中,而每條線程有自己的工作內存(本地內存),工作內存保存了共享變量的副本,而不同內存又無法訪問對方的工作內存,所以假如線程在工作內存中修改了變量副本,其它線程是無從得知的。

線程的傳值均需要通過主內存來完成

主內存與工作內存如何交互?

Java內存模型定義了8種操作來完成主內存與工作內存的交互細節,虛擬機必需保證這8種操作的每一個操作都是原子的,不可再分的。

lock: 作用于主內存的變量,把變量標識為線程獨占的狀態

unlock: 與lock對應,把主內存中處于鎖定狀態的變量釋放出來,釋放后的變量才可以被其余線程鎖定。

read: 作用于主內存的變量,把一個變量的值從主內存傳輸到線程的工作內存,便于隨后的load使用。

load:作用于工作內存的變量,把read讀取到的變量放入工作內存副本

use: 作用于工作內存,把工作內存的變量值傳遞給執行引擎,每當虛擬機遇到一個需要使用到變量的值的字節碼指令時將會執行這個操作。

assign: 作用于工作內存,把執行引擎收到的值賦給工作內存的變量,虛擬機遇到賦值字節碼時候執行這個操作

store:作用于工作內存,把變量的值傳輸到住內存中,以便隨后的write使用

write:作用于主內存,把store操作從工作內存得到的值放入主內存的變量中。

JMM內存模型

執行上述8種基本操作的規則:

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

不允許一個線程丟棄它最近的assign操作。即變量在工作內存中改變了賬號必需把變化同步回主內存

一個新的變量只允許在主內存中誕生,不允許工作內存直接使用未初始化的變量。

一個變量同一時刻只允許一條線程進行lock操作,但同一線程可以lock屢次,lock屢次之后必需執行同樣次數的unlock操作

假如對一個變量進行lock操作,那么將會清空工作內存中此變量的值。

不允許對未lock的變量進行unlock操作,也不允許unlock一個被其它線程lock的變量

假如一個變量執行unlock操作,必需先把次變了同步回主內存中。

這8種操作定義相當嚴禁,實踐起來又比較麻煩,但是可以有助于我們了解多線程的工作原理。有一個與此8種操作相等的Happen-before準則。

Happen-before準則

這個是Java內存模型下無需任何同步器協助就已經存在,可以直接在編碼中使用。假如兩個操作之間的關系不在此列,并且無法從下列規則推導出來的話,它們的順序就沒有保障,虛擬機可以對他們進行任意的重排。

天然的happen-before

程序順序準則:一個線程內包裝語義的串行性

volatile變量的寫,先發生于讀,這保證了volatile變量的可見性

鎖規則:unlock先與lock

傳遞性:A 先于B,B先于C,那么A必然先于C

線程的start先于線程的每一個動作

線程的所有操作優先于線程的終結(Thread.join())

線程的中斷(interupt)先于被中斷線程的代碼

對象的構造函數執行,先于finalize()方法

Java運行時數據區

JVM定義了少量程序運行時會使用到的運行時數據區,其中少量會隨著虛擬機啟動而創立,隨著虛擬機退出而銷毀。另外少量是與現場逐個對應的,這些線程對應的數據區會隨著線程的開始和結束而創立和銷毀。

這部分參考JVM規范

1. pc寄存器

可以支持多條線程同時允許,每一條Java虛擬機線程都有自己的pc寄存器。任意時刻,一條JVM線程之后執行一個方法的代碼,這個方法被稱為當前方法(current method)

假如這個方法不是native的,那么PC寄存器就保存JVM正在執行的字節碼指令地址。

假如是native的,那么pc寄存器的值為undefined

pc寄存器的容量至少能保證一個returnAddress類型的數據或者者一個平臺無關的本地指針的值。

2. JVM Stack(虛擬機棧)

每一個JVM線程都有自己的私有虛擬機棧,這個棧與線程同時創立,用于存儲棧幀(Frame)。

棧用來存儲局部變量與少量過程結果的地方。在方法調用和返回中也扮演了很重要的角色。

棧可以試固定分配的也可以動態調整

假如請求線程分配的容量超過JVM棧允許的最大容量,拋出StackOverflowError異常

假如JVM棧可以動態擴展,擴展的動作也已經嘗試過,但是沒有申請到足夠的內存,則拋出OutofMemoryError異常

3. Heap(堆)

堆是可以可供各個線程共享的運行時存儲區域,也是供所有類的實例和數組對象分配內存的區域。堆在JVM啟動的時候創立。

堆所存儲的就是被GC所管理的各種對象。

堆也是可以固定大小和動態調整的:

實際所需的堆超過的GC所提供的最大容量,那么JVM拋出OutofMemoryError異常。

4. Method Area(方法區)

也是各個線程共享的運行時內存區,它存儲每一個類的實例信息,運行時常量池,字段和方法數據,構造函數和普通方法的字節碼等內容。還有少量特殊方法。

方法區是堆的邏輯組成部分,也在JVM啟動時創立,簡單的JVM可以不實現這個區域的垃圾收集。

方法區也可固定大小和動態分配與堆一樣,內存空間不夠,那么JVM拋出OutofMemoryError異常。

5. Run-Time Constant Pool(運行時常量池)

在方法區中分配,在加載類和接口到虛擬機之后,就創立對應的運行時常量池。

它是class文件中每一個類或者接口的常量池表的運行時體現形式。像字符串。Java的主要類型。

存儲區域不夠用時候拋出OutofMemoryError異常。

6. Native Method Stacks(原生方法棧或者本地方法棧)

JDK中native的方法,System類和Thread類中有很多。使用C語言編寫的方法,這個也通常叫做C stack。

可以不支持本地方法棧,但是假如支持的時候,這個棧一般會在線程創立的時候按線程分配。

與棧的錯誤一樣,StackOverFlowError和OutOfMemeoryError.

為了讓學習變得輕松、高效,今天給大家免費分享一套Java入門教學資源。幫助大家在成為Java架構師的道路上披荊斬棘。需要入門的資料歡迎加入學習交流群:9285,05736

  • 全部評論(0)
最新發布的資訊信息
【系統環境|服務器應用】Discuz發布帖子時默認顯示第一個主題分類的修改方法(2019-12-09 00:13)
【系統環境|軟件環境】Android | App內存優化 之 內存泄漏 要點概述 以及 處理實戰(2019-12-04 14:27)
【系統環境|軟件環境】MySQL InnoDB 事務(2019-12-04 14:26)
【系統環境|軟件環境】vue-router(單頁面應用控制中心)常見用法(2019-12-04 14:26)
【系統環境|軟件環境】Linux中的Kill命令(2019-12-04 14:26)
【系統環境|軟件環境】Linux 入門時必學60個文件解決命令(2019-12-04 14:26)
【系統環境|軟件環境】更新版ThreeJS 3D粒子波浪動畫(2019-12-04 14:26)
【系統環境|軟件環境】前臺開發WebStorm常用快捷鍵,火速收藏!(2019-12-04 14:25)
【系統環境|軟件環境】微博H5登錄和發微博組件(2019-12-04 14:25)
【系統環境|軟件環境】5分鐘談前臺面試,小伙伴都驚呆了(2019-12-04 14:23)
手機二維碼手機訪問領取大禮包
返回頂部
澳洲幸运10精准人工计划