1.什么是JMM?
? JMM 是Java內(nèi)存模型( Java Memory Model),簡稱JMM。它本身只是一個(gè)抽象的概念,并不真實(shí)存在,它描述的是一種規(guī)則或規(guī)范,是和多線程相關(guān)的一組規(guī)范。通過這組規(guī)范,定義了程序中對(duì)各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問方式。需要每個(gè)JVM 的實(shí)現(xiàn)都要遵守這樣的規(guī)范,有了JMM規(guī)范的保障,并發(fā)程序運(yùn)行在不同的虛擬機(jī)上時(shí),得到的程序結(jié)果才是安全可靠可信賴的。如果沒有JMM 內(nèi)存模型來規(guī)范,就可能會(huì)出現(xiàn),經(jīng)過不同 JVM 翻譯之后,運(yùn)行的結(jié)果不相同也不正確的情況。
? 計(jì)算機(jī)在執(zhí)行程序時(shí),每條指令都是在CPU中執(zhí)行的。而執(zhí)行指令的過程中,勢(shì)必涉及到數(shù)據(jù)的讀取和寫入。由于程序運(yùn)行過程中的臨時(shí)數(shù)據(jù)是存放在主存(物理內(nèi)存)當(dāng)中的,這時(shí)就存在一個(gè)問題,由于CPU執(zhí)行速度很快,而從內(nèi)存讀取數(shù)據(jù)和向內(nèi)存寫入數(shù)據(jù)的過程,跟CPU執(zhí)行指令的速度比起來要慢的多(硬盤 < 內(nèi)存 <緩存cache < CPU)。因此如果任何時(shí)候?qū)?shù)據(jù)的操作都要通過和內(nèi)存的交互來進(jìn)行,會(huì)大大降低指令執(zhí)行的速度。因此在CPU里面就有了高速緩存。也就是當(dāng)程序在運(yùn)行過程中,會(huì)將運(yùn)算需要的數(shù)據(jù)從主存復(fù)制一份到CPU的高速緩存當(dāng)中,那么CPU進(jìn)行計(jì)算時(shí),就可以直接從它的高速緩存中讀取數(shù)據(jù)或向其寫入數(shù)據(jù)了。當(dāng)運(yùn)算結(jié)束之后,再將高速緩存中的數(shù)據(jù)刷新到主存當(dāng)中。
JMM 抽象出主存儲(chǔ)器(Main Memory)和工作存儲(chǔ)器(Working Memory)兩種。
·主存儲(chǔ)器是實(shí)例對(duì)象所在的區(qū)域,所有的實(shí)例都存在于主存儲(chǔ)器內(nèi)。比如,實(shí)例所擁有的字段即位于主存儲(chǔ)器內(nèi),主存儲(chǔ)器是所有的線程所共享的。
·工作存儲(chǔ)器是線程所擁有的作業(yè)區(qū),每個(gè)線程都有其專用的工作存儲(chǔ)器。工作存儲(chǔ)器存有主存儲(chǔ)器中必要部分的拷貝,稱之為工作拷貝(Working Copy)。
所以,線程無法直接對(duì)主內(nèi)存進(jìn)行操作,此外,線程A想要和線程B通信,只能通過主存進(jìn)行。
2.JMM的三大特性:
JMM的三大特性:原子性、可見性、有序性。
1.原子性
一個(gè)或多個(gè)操作,要么全部執(zhí)行,要么全部不執(zhí)行(執(zhí)行的過程中是不會(huì)被任何因素打斷的)。
2.可見性
只要有一個(gè)線程對(duì)共享變量的值做了修改,其他線程都將馬上收到通知,立即獲得最新值。
3.有序性
? 有序性可以總結(jié)為:在本線程內(nèi)觀察,所有的操作都是有序的;而在一個(gè)線程內(nèi)觀察另一個(gè)線程,所有操作都是無序的。前半句指 as-if-serial 語義:線程內(nèi)似表現(xiàn)為串行,后半句是指:“指令重排序現(xiàn)象”和“工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象”。處理器為了提高程序的運(yùn)行效率,提高并行效率,可能會(huì)對(duì)代碼進(jìn)行優(yōu)化。編譯器認(rèn)為,重排序后的代碼執(zhí)行效率更優(yōu)。這樣一來,代碼的執(zhí)行順序就未必是編寫代碼時(shí)候的順序了,在多線程的情況下就可能會(huì)出錯(cuò)。
? 在代碼順序結(jié)構(gòu)中,我們可以直觀的指定代碼的執(zhí)行順序, 即從上到下按序執(zhí)行。但編譯器和CPU處理器會(huì)根據(jù)自己的決策,對(duì)代碼的執(zhí)行順序進(jìn)行重新排序,優(yōu)化指令的執(zhí)行順序,提升程序的性能和執(zhí)行速度,使語句執(zhí)行順序發(fā)生改變,出現(xiàn)重排序,但最終結(jié)果看起來沒什么變化(在單線程情況下)。
? 有序性問題 指的是在多線程的環(huán)境下,由于執(zhí)行語句重排序后,重排序的這一部分沒有一起執(zhí)行完,就切換到了其它線程,導(dǎo)致計(jì)算結(jié)果與預(yù)期不符的問題。這就是編譯器的編譯優(yōu)化給并發(fā)編程帶來的程序有序性問題。
Java 語言提供了 volatile 和 synchronized 兩個(gè)關(guān)鍵字來保證線程之間操作的有序性,volatile 是因?yàn)槠浔旧戆敖怪噶钪嘏判颉钡恼Z義,synchronized 是由“一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對(duì)其進(jìn)行 lock 操作”這條規(guī)則獲得的,此規(guī)則決定了持有同一個(gè)對(duì)象鎖的兩個(gè)同步塊只能串行進(jìn)入。
3.關(guān)于同步的規(guī)定:
1.線程解鎖前,必須把共享變量的值刷新回主內(nèi)存。
2.線程加鎖前,必須將主內(nèi)存的最新值讀取到自己的工作內(nèi)存。
3.加鎖解鎖是同一把鎖。
4.解釋說明
? 在JVM中,棧負(fù)責(zé)運(yùn)行(主要是方法),堆負(fù)責(zé)存儲(chǔ)(比如new的對(duì)象)。由于JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程在創(chuàng)建時(shí),JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為??臻g),工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域。而JAVA內(nèi)存模型中規(guī)定,所有變量都存儲(chǔ)在主內(nèi)存中,主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問。
? 但線程對(duì)變量的操作(讀取賦值等)必須在自己的工作內(nèi)存中進(jìn)行。首先要將變量從主內(nèi)存拷貝到自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后,再將變量寫回到主內(nèi)存。由于不能直接操作主內(nèi)存中的變量,各個(gè)線程的工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本,因此,不同的線程之間無法直接訪問對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過主內(nèi)存來完成。
JMM中的八種操作:
為了支持 JMM,Java 定義了8種原子操作,用來控制主存與工作內(nèi)存之間的交互:文章來源:http://www.zghlxwxcb.cn/news/detail-402845.html
·read 讀取:作用于主內(nèi)存,將共享變量從主內(nèi)存?zhèn)魉偷骄€程的工作內(nèi)存中。
·load 載入:作用于工作內(nèi)存,把 read 讀取的值放到工作內(nèi)存中的副本變量中。
·store 存儲(chǔ):作用于工作內(nèi)存,把工作內(nèi)存中的變量傳送到主內(nèi)存中。
·write 寫入:作用于主內(nèi)存,把從工作內(nèi)存中 store 傳送過來的值寫到主內(nèi)存的變量中。
·use 使用:作用于工作內(nèi)存,把工作內(nèi)存的值傳遞給執(zhí)行引擎,當(dāng)虛擬機(jī)遇到一個(gè)需要使用這個(gè)變量的指令時(shí),就會(huì)執(zhí)行這個(gè)動(dòng)作。
·assign 賦值:作用于工作內(nèi)存,把執(zhí)行引擎獲取到的值賦值給工作內(nèi)存中的變量,當(dāng)虛擬機(jī)棧遇到給變量賦值的指令時(shí),就執(zhí)行此操作。
·lock鎖定: 作用于主內(nèi)存,把變量標(biāo)記為線程獨(dú)占狀態(tài)。
·unlock解鎖: 作用于主內(nèi)存,它將釋放獨(dú)占狀態(tài)。文章來源地址http://www.zghlxwxcb.cn/news/detail-402845.html
到了這里,關(guān)于JMM(Java內(nèi)存模型)詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!