Java的多线程以及内存模型的知识点梳理,有想到过这些吗?
JMM大致描述:
JMM描述了線程如何與內(nèi)存進(jìn)行交互。Java虛擬機(jī)規(guī)范視圖定義一種Java內(nèi)存模型,來(lái)屏蔽掉各種操作系統(tǒng)內(nèi)存訪問(wèn)的差異,以實(shí)現(xiàn)Java程序在各種平臺(tái)下都能達(dá)到一致的訪問(wèn)效果。
JMM描述了JVM如何與計(jì)算機(jī)的內(nèi)存進(jìn)行交互
JMM都是圍繞著原子性,有序性和可見性進(jìn)行展開的
JMM的主要目標(biāo)是定義程序中各個(gè)變量的訪問(wèn)規(guī)則,虛擬機(jī)將變量存儲(chǔ)到內(nèi)存和從內(nèi)存取出變量這樣的底層細(xì)節(jié)。此處的變量指在堆中存儲(chǔ)的元素。
多線程的時(shí)候?yàn)槭裁慈菀壮鲥e(cuò)?
Java內(nèi)存模型規(guī)定所有的共享變量都存儲(chǔ)在主內(nèi)存中,而每條線程有自己的工作內(nèi)存(本地內(nèi)存),工作內(nèi)存保存了共享變量的副本,而不同內(nèi)存又無(wú)法訪問(wèn)對(duì)方的工作內(nèi)存,所以如果線程在工作內(nèi)存中修改了變量副本,其它線程是無(wú)從得知的。
線程的傳值均需要通過(guò)主內(nèi)存來(lái)完成
主內(nèi)存與工作內(nèi)存如何交互?
Java內(nèi)存模型定義了8種操作來(lái)完成主內(nèi)存與工作內(nèi)存的交互細(xì)節(jié),虛擬機(jī)必須保證這8種操作的每一個(gè)操作都是原子的,不可再分的。
lock: 作用于主內(nèi)存的變量,把變量標(biāo)識(shí)為線程獨(dú)占的狀態(tài)
unlock: 與lock對(duì)應(yīng),把主內(nèi)存中處于鎖定狀態(tài)的變量釋放出來(lái),釋放后的變量才可以被其他線程鎖定。
read: 作用于主內(nèi)存的變量,把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存,便于隨后的load使用。
load:作用于工作內(nèi)存的變量,把read讀取到的變量放入工作內(nèi)存副本
use: 作用于工作內(nèi)存,把工作內(nèi)存的變量值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作。
assign: 作用于工作內(nèi)存,把執(zhí)行引擎收到的值賦給工作內(nèi)存的變量,虛擬機(jī)遇到賦值字節(jié)碼時(shí)候執(zhí)行這個(gè)操作
store:作用于工作內(nèi)存,把變量的值傳輸?shù)阶?nèi)存中,以便隨后的write使用
write:作用于主內(nèi)存,把store操作從工作內(nèi)存得到的值放入主內(nèi)存的變量中。
JMM內(nèi)存模型
執(zhí)行上述8種基本操作的規(guī)則:
不允許read和load,store和write操作之一單獨(dú)出現(xiàn)。
不允許一個(gè)線程丟棄它最近的assign操作。即變量在工作內(nèi)存中改變了賬號(hào)必須把變化同步回主內(nèi)存
一個(gè)新的變量只允許在主內(nèi)存中誕生,不允許工作內(nèi)存直接使用未初始化的變量。
一個(gè)變量同一時(shí)刻只允許一條線程進(jìn)行l(wèi)ock操作,但同一線程可以lock多次,lock多次之后必須執(zhí)行同樣次數(shù)的unlock操作
如果對(duì)一個(gè)變量進(jìn)行l(wèi)ock操作,那么將會(huì)清空工作內(nèi)存中此變量的值。
不允許對(duì)未lock的變量進(jìn)行unlock操作,也不允許unlock一個(gè)被其它線程lock的變量
如果一個(gè)變量執(zhí)行unlock操作,必須先把次變了同步回主內(nèi)存中。
這8種操作定義相當(dāng)嚴(yán)禁,實(shí)踐起來(lái)又比較麻煩,但是可以有助于我們理解多線程的工作原理。有一個(gè)與此8種操作相等的Happen-before原則。
Happen-before原則
這個(gè)是Java內(nèi)存模型下無(wú)需任何同步器協(xié)助就已經(jīng)存在,可以直接在編碼中使用。如果兩個(gè)操作之間的關(guān)系不在此列,并且無(wú)法從下列規(guī)則推導(dǎo)出來(lái)的話,它們的順序就沒(méi)有保障,虛擬機(jī)可以對(duì)他們進(jìn)行任意的重排。
天然的happen-before
程序順序原則:一個(gè)線程內(nèi)包裝語(yǔ)義的串行性
volatile變量的寫,先發(fā)生于讀,這保證了volatile變量的可見性
鎖規(guī)則:unlock先與lock
傳遞性:A 先于B,B先于C,那么A必然先于C
線程的start先于線程的每一個(gè)動(dòng)作
線程的所有操作優(yōu)先于線程的終結(jié)(Thread.join())
線程的中斷(interupt)先于被中斷線程的代碼
對(duì)象的構(gòu)造函數(shù)執(zhí)行,先于finalize()方法
Java運(yùn)行時(shí)數(shù)據(jù)區(qū)
JVM定義了一些程序運(yùn)行時(shí)會(huì)使用到的運(yùn)行時(shí)數(shù)據(jù)區(qū),其中一些會(huì)隨著虛擬機(jī)啟動(dòng)而創(chuàng)建,隨著虛擬機(jī)退出而銷毀。另外一些是與現(xiàn)場(chǎng)一一對(duì)應(yīng)的,這些線程對(duì)應(yīng)的數(shù)據(jù)區(qū)會(huì)隨著線程的開始和結(jié)束而創(chuàng)建和銷毀。
這部分參考JVM規(guī)范
1. pc寄存器
可以支持多條線程同時(shí)允許,每一條Java虛擬機(jī)線程都有自己的pc寄存器。任意時(shí)刻,一條JVM線程之后執(zhí)行一個(gè)方法的代碼,這個(gè)方法被稱為當(dāng)前方法(current method)
如果這個(gè)方法不是native的,那么PC寄存器就保存JVM正在執(zhí)行的字節(jié)碼指令地址。
如果是native的,那么pc寄存器的值為undefined
pc寄存器的容量至少能保證一個(gè)returnAddress類型的數(shù)據(jù)或者一個(gè)平臺(tái)無(wú)關(guān)的本地指針的值。
2. JVM Stack(虛擬機(jī)棧)
每一個(gè)JVM線程都有自己的私有虛擬機(jī)棧,這個(gè)棧與線程同時(shí)創(chuàng)建,用于存儲(chǔ)棧幀(Frame)。
棧用來(lái)存儲(chǔ)局部變量與一些過(guò)程結(jié)果的地方。在方法調(diào)用和返回中也扮演了很重要的角色。
棧可以試固定分配的也可以動(dòng)態(tài)調(diào)整
如果請(qǐng)求線程分配的容量超過(guò)JVM棧允許的最大容量,拋出StackOverflowError異常
如果JVM棧可以動(dòng)態(tài)擴(kuò)展,擴(kuò)展的動(dòng)作也已經(jīng)嘗試過(guò),但是沒(méi)有申請(qǐng)到足夠的內(nèi)存,則拋出OutofMemoryError異常
3. Heap(堆)
堆是可以可供各個(gè)線程共享的運(yùn)行時(shí)存儲(chǔ)區(qū)域,也是供所有類的實(shí)例和數(shù)組對(duì)象分配內(nèi)存的區(qū)域。堆在JVM啟動(dòng)的時(shí)候創(chuàng)建。
堆所存儲(chǔ)的就是被GC所管理的各種對(duì)象。
堆也是可以固定大小和動(dòng)態(tài)調(diào)整的:
實(shí)際所需的堆超過(guò)的GC所提供的最大容量,那么JVM拋出OutofMemoryError異常。
4. Method Area(方法區(qū))
也是各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū),它存儲(chǔ)每一個(gè)類的實(shí)例信息,運(yùn)行時(shí)常量池,字段和方法數(shù)據(jù),構(gòu)造函數(shù)和普通方法的字節(jié)碼等內(nèi)容。還有一些特殊方法。
方法區(qū)是堆的邏輯組成部分,也在JVM啟動(dòng)時(shí)創(chuàng)建,簡(jiǎn)單的JVM可以不實(shí)現(xiàn)這個(gè)區(qū)域的垃圾收集。
方法區(qū)也可固定大小和動(dòng)態(tài)分配與堆一樣,內(nèi)存空間不夠,那么JVM拋出OutofMemoryError異常。
5. Run-Time Constant Pool(運(yùn)行時(shí)常量池)
在方法區(qū)中分配,在加載類和接口到虛擬機(jī)之后,就創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)常量池。
它是class文件中每一個(gè)類或接口的常量池表的運(yùn)行時(shí)表現(xiàn)形式。像字符串。Java的主要類型。
存儲(chǔ)區(qū)域不夠用時(shí)候拋出OutofMemoryError異常。
6. Native Method Stacks(原生方法棧或本地方法棧)
JDK中native的方法,System類和Thread類中有很多。使用C語(yǔ)言編寫的方法,這個(gè)也通常叫做C stack。
可以不支持本地方法棧,但是如果支持的時(shí)候,這個(gè)棧一般會(huì)在線程創(chuàng)建的時(shí)候按線程分配。
與棧的錯(cuò)誤一樣,StackOverFlowError和OutOfMemeoryError.
為了讓學(xué)習(xí)變得輕松、高效,今天給大家免費(fèi)分享一套Java入門教學(xué)資源。幫助大家在成為Java架構(gòu)師的道路上披荊斬棘。需要入門的資料歡迎加入學(xué)習(xí)交流群:9285,05736
總結(jié)
以上是生活随笔為你收集整理的Java的多线程以及内存模型的知识点梳理,有想到过这些吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 初学Java编程经常遇到的问题,你们遇到
- 下一篇: 大家对Java的一些误解