Java的多线程以及内存模型的知识点梳理,有想到过这些吗?
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
總結
以上是生活随笔為你收集整理的Java的多线程以及内存模型的知识点梳理,有想到过这些吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初学Java编程经常遇到的问题,你们遇到
- 下一篇: postscript怎么打开_怎么把在学