摘要:講什么內(nèi)存模型描述了多個(gè)線程之間通過(guò)內(nèi)存交互的規(guī)范,屏蔽了各種硬件和操作系統(tǒng)的訪問(wèn)差異的,保證了程序在各種平臺(tái)下對(duì)內(nèi)存的訪問(wèn)都能保證效果一致。這個(gè)版本的內(nèi)存模型在中仍然在使用。
JMM講什么
內(nèi)存模型(Memory Model)描述了多個(gè)線程之間通過(guò)內(nèi)存交互的規(guī)范,屏蔽了各種硬件和操作系統(tǒng)的訪問(wèn)差異的,保證了Java程序在各種平臺(tái)下對(duì)內(nèi)存的訪問(wèn)都能保證效果一致。在現(xiàn)代的多處理器(多核處理器)系統(tǒng)中,處理器擁有多級(jí)緩存以提升內(nèi)存訪問(wèn)速度同時(shí)減少了內(nèi)存總線的訪問(wèn)量。變量最終會(huì)保存在內(nèi)存中,但是編譯器、運(yùn)行時(shí)、處理器可以對(duì)指令優(yōu)化和重新排序,緩存、寄存器也對(duì)內(nèi)存進(jìn)行了讀寫優(yōu)化,只要保證在單個(gè)線程內(nèi)行為與代碼順序串行語(yǔ)義相同即可。內(nèi)存模型定義了充分且必要的條款,描述了程序中變量之間的關(guān)系,以及變量的讀取、寫入的底層細(xì)節(jié),實(shí)現(xiàn)了并發(fā)過(guò)程中的原子性、可見(jiàn)性、有序性。
老版本JMM中的問(wèn)題原始的Java內(nèi)存模型存在一些不足,因此Java內(nèi)存模型在Java 1.5時(shí)被重新修訂(JSR133)。這個(gè)版本的Java內(nèi)存模型在Java 8中仍然在使用。老版本中的問(wèn)題有:
final字段的值并不是完全不變的。構(gòu)造器中對(duì)final字段值的寫入可以重排序至構(gòu)造函數(shù)返回并將對(duì)象引用賦值給變量之后,導(dǎo)致其它線程看到還未完成初始化的final字段。這個(gè)問(wèn)題的經(jīng)典案例是String的早期實(shí)現(xiàn)中,有多個(gè)final字段,但是其它線程可以看到字符串長(zhǎng)度為0,而實(shí)際上字符串長(zhǎng)度并不為0。
volatile字段的寫操作與非volatile字段的讀寫操作重排序。由于重排序的緣故,volatile字段的寫操作之前的操作被重新排序至之后進(jìn)行,導(dǎo)致其它線程看到的結(jié)果與程序代碼不一致。這個(gè)問(wèn)題的經(jīng)典案例是Double-Checked Locking(也稱multi-threaded singleton pattern)問(wèn)題,即通過(guò)一個(gè)volatile字段來(lái)判斷是否需要進(jìn)行初始化,從而實(shí)現(xiàn)延遲初始化并減少鎖操作的性能損耗。由于重排序的問(wèn)題,導(dǎo)致部分初始化操作或構(gòu)造操作被排序至volatile字段寫操作之后,導(dǎo)致其它線程看到部分初始化的數(shù)據(jù),破壞了數(shù)據(jù)的一致性。
JSR133解決的問(wèn)題
volatile語(yǔ)義增強(qiáng):volatile字段的讀、寫操作與其它任務(wù)內(nèi)存操作操作重排序,volatile的讀操作與監(jiān)視器鎖的獲取具有相同的內(nèi)存語(yǔ)義(緩存失效并從主存重新讀取),volatile的定操作與監(jiān)視器鎖的釋放具有相同的內(nèi)存語(yǔ)義(緩存刷入主存)。在這個(gè)約定下,線程A寫入volatile字段V后,線程B可以讀出V的值,同時(shí)線程A在寫入V時(shí)能夠看到的變量值對(duì)線程B也可見(jiàn)。
是否可以重排序 | 第二個(gè)操作 | 第二個(gè)操作 | 第二個(gè)操作 |
---|---|---|---|
第一個(gè)操作 | 普通讀/普通寫 | volatile讀/monitor enter | volatile寫/monitor exit |
普通讀/普通寫 | No | ||
voaltile讀/monitor enter | No | No | No |
volatile寫/monitor exit | No | No |
其中普通讀指getfield, getstatic, 非volatile數(shù)組的arrayload, 普通寫指putfield, putstatic, 非volatile數(shù)組的arraystore。volatile讀寫分別是volatile字段的getfield, getstatic和putfield, putstatic。monitorenter是進(jìn)入同步塊或同步方法,monitorexist指退出同步塊或同步方法。
final字段增強(qiáng):只要對(duì)象正確構(gòu)造,那么不需要使用同步就可以保證任意線程都能看到final字段在構(gòu)造器中被初始化之后的值。編譯器會(huì)在final域的寫之后,構(gòu)造器函數(shù)return之前,插入StoreStore屏障。這個(gè)屏障禁止處理器把final域的寫重排序到構(gòu)造函數(shù)之外。寫final域的重排序規(guī)則可以保證:在對(duì)象引用為任意線程可見(jiàn)之前,對(duì)象的final域已經(jīng)被正確初始化過(guò)了,而普通域不具有這個(gè)保障。讀final域的重排序規(guī)則是,禁止處理器重排序初次讀取對(duì)象引用與初次讀取該對(duì)象包含的final域這兩個(gè)操作。編譯器會(huì)在讀final域操作的前面插入一個(gè)LoadLoad屏障。讀final域的重排序規(guī)則可以確保:在讀一個(gè)對(duì)象的final域之前,一定會(huì)先讀包含這個(gè)final域的對(duì)象的引用。
初次讀對(duì)象引用與初次讀該對(duì)象包含的final域,這兩個(gè)操作之間存在間接依賴關(guān)系。由于編譯器遵守間接依賴關(guān)系,因此不會(huì)重排序這兩個(gè)操作;大多數(shù)處理器也會(huì)遵守間接依賴,也不會(huì)重排序這兩個(gè)操作。但有少數(shù)處理器允許對(duì)存在間接依賴關(guān)系的操作做重排序(比如alpha處理器),這個(gè)規(guī)則就是專門用來(lái)針對(duì)這種處理器的。
對(duì)于引用類型,寫final域的重排序規(guī)則對(duì)編譯器和處理器增加了如下約束:
在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final引用的對(duì)象的成員的寫入,與隨后在構(gòu)造函數(shù)外把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序
確保在final引用的初始化在構(gòu)造函數(shù)內(nèi)完成,因此一旦其他線程拿到了一個(gè)非null的final引用,那么這個(gè)引用一定是在構(gòu)造函數(shù)內(nèi)被正確賦值的(至于是否正確初始化,則不一定,這取決于final引用的賦值語(yǔ)句)
Happens-Before規(guī)則HappendBefore規(guī)則包括:
程序順序規(guī)則: 如果程序中操作A在操作B之前,那么同一個(gè)線程中操作A將在操作B之前進(jìn)行
監(jiān)視器鎖規(guī)則: 在監(jiān)視器鎖上的鎖操作必須在同一個(gè)監(jiān)視器鎖上的加鎖操作之前執(zhí)行
volatile變量規(guī)則: volatile變量的寫入操作必須在該變量的讀操作之前執(zhí)行
線程啟動(dòng)規(guī)則: 在線程上對(duì)Thread.start的調(diào)用必須在該線程中執(zhí)行任何操作之前執(zhí)行
線程結(jié)束規(guī)則: 線程中的任何操作都必須在其他線程檢測(cè)到該線程已經(jīng)結(jié)束之前執(zhí)行
中斷規(guī)則: 當(dāng)一個(gè)線程在另一個(gè)線程上調(diào)用interrupt時(shí),必須在被中斷線程檢測(cè)到interrupt之前執(zhí)行
傳遞性: 如果操作A在操作B之前執(zhí)行,并且操作B在操作C之前執(zhí)行,那么操作A在操作C之前執(zhí)行
相關(guān)鏈接https://zhuanlan.zhihu.com/p/29881777
http://www.cs.umd.edu/~pugh/java/memoryModel/
https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf
http://gee.cs.oswego.edu/dl/jmm/cookbook.html
https://liuzhengyang.github.io/2017/05/12/javamemorymodel/
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/73961.html
摘要:編譯器,和處理器會(huì)共同確保單線程程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果相同。正確同步的多線程程序的執(zhí)行將具有順序一致性程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同。 前情提要 深入理解Java內(nèi)存模型(六)——final 處理器內(nèi)存模型 順序一致性內(nèi)存模型是一個(gè)理論參考模型,JMM和處理器內(nèi)存模型在設(shè)計(jì)時(shí)通常會(huì)把順序一致性內(nèi)存模型作為參照。JMM和處理器內(nèi)...
摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過(guò)一遍,現(xiàn)在學(xué)的東西越多,感覺(jué)那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對(duì)順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://segmentfault.com/img/remote/1460000013474312?w=1920&h=1271); 前提 《深...
摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過(guò)一遍,現(xiàn)在學(xué)的東西越多,感覺(jué)那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對(duì)順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6RtPu3BNx3zps1JhSmPICRw7QgeOmxOfTb...
摘要:內(nèi)存模型對(duì)內(nèi)存模型的介紹對(duì)內(nèi)存模型的結(jié)構(gòu)圖的線程之間的通信是通過(guò)共享內(nèi)存的方式進(jìn)行隱式通信,即線程把某狀態(tài)寫入主內(nèi)存中的共享變量,線程讀取的值,這樣就完成了通信。 Java內(nèi)存模型(JMM) 1.對(duì)內(nèi)存模型的介紹 ①對(duì)Java內(nèi)存模型的結(jié)構(gòu)圖 java的線程之間的通信是通過(guò)共享內(nèi)存的方式進(jìn)行隱式通信,即線程A把某狀態(tài)寫入主內(nèi)存中的共享變量X,線程B讀取X的值,這樣就完成了通信。是一種...
摘要:順序一致性內(nèi)存模型有兩大特性一個(gè)線程中所有操作必須按照程序的順序執(zhí)行。這里的同步包括對(duì)常用同步原語(yǔ)的正確使用通過(guò)以下程序說(shuō)明與順序一致性兩種內(nèi)存模型的對(duì)比順序一致性模型中所有操作完全按程序的順序串行執(zhí)行。 java內(nèi)存模型 java內(nèi)存模型基礎(chǔ) happen-before模型 JSR-133使用happen-before的概念來(lái)闡述操作之間的內(nèi)存可見(jiàn)性。在JMM中,如果一個(gè)操作執(zhí)行的結(jié)...
摘要:下面是該程序在兩個(gè)內(nèi)存模型中的執(zhí)行時(shí)序?qū)Ρ葓D在順序一致性模型中,所有操作完全按程序的順序串行執(zhí)行。不保證未同步程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果一致。 前情提要 深入理解Java內(nèi)存模型(二)——重排序 數(shù)據(jù)競(jìng)爭(zhēng)與順序一致性保證 當(dāng)程序未正確同步時(shí),就會(huì)存在數(shù)據(jù)競(jìng)爭(zhēng)。java內(nèi)存模型規(guī)范對(duì)數(shù)據(jù)競(jìng)爭(zhēng)的定義如下: 在一個(gè)線程中寫一個(gè)變量, 在另一個(gè)線程讀同一個(gè)變量,...
閱讀 2435·2021-10-09 09:44
閱讀 3792·2021-09-22 15:43
閱讀 2924·2021-09-02 09:47
閱讀 2539·2021-08-12 13:29
閱讀 3871·2019-08-30 15:43
閱讀 1680·2019-08-30 13:06
閱讀 2189·2019-08-29 16:07
閱讀 2745·2019-08-29 15:23