先看計算機(jī)硬件
cpu的運(yùn)行并不是直接操作內(nèi)存的,而是先把內(nèi)存中的數(shù)據(jù)讀到緩存,而內(nèi)存中的讀和寫操作的時候就會造成不一致的問題。
JVM規(guī)范中試圖定義一種java內(nèi)存模型JMM,來屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實現(xiàn)讓java程序在各種平臺下都能達(dá)到一致的內(nèi)存訪問效果。
JMM是一種抽象的概念并不真實的存在,它僅僅描述的是一組約定或者規(guī)范,通過規(guī)范定義了程序中尤其是多線程時各個變量的讀寫訪問方式,并決定一個線程對共享變量的寫入何時以及如何變成對另一個線程可見,關(guān)鍵技術(shù)點都是圍繞著多線程的原子性、可見性、有序性展開的。
jmm能干嘛呢?
1 通過jmm來實現(xiàn)線程和主內(nèi)存之間的抽象關(guān)系。
2 屏蔽各個硬件平臺和操作系統(tǒng)的內(nèi)存訪問差異以實現(xiàn)讓java程序在各種平臺下都能達(dá)到一致的內(nèi)存訪問效果。
可見性是指當(dāng)一個線程修改了某一個共享變量的值,其他線程是否能夠立即知道變更,jmm規(guī)定了所有變量都存儲在主內(nèi)存中。
系統(tǒng)主內(nèi)存共享變量數(shù)據(jù)修改被寫入的時機(jī)時不確定的,多線程并發(fā)下很可能出現(xiàn)臟讀,jmm規(guī)定每個線程都有自己的工作內(nèi)存,線程自己的工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在線程自己的工作內(nèi)存中進(jìn)行,而不能夠直接讀寫主內(nèi)存中的變量。不同線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量值的傳遞均需要通過主內(nèi)存來完成。
上述的操作將變量x的值寫丟了一次,出現(xiàn)臟讀。
原子性指一個操作是不可被打斷的,即多線程環(huán)境下,操作不能被其他線程干擾。
有序性對于一個線程的執(zhí)行代碼而言,我們總是習(xí)慣認(rèn)為代碼的執(zhí)行總是從上到下,有序執(zhí)行。
但是為了提升性能,編譯器和處理器通常會對指令序列進(jìn)行重新排序。Java規(guī)范規(guī)定jvm線程內(nèi)部維持順序化語義,即只要程序的最終結(jié)果與它順序化執(zhí)行的結(jié)果相等,那么指令的執(zhí)行順序可以與代碼順序不一致,此過程叫指令的重排序。
優(yōu)缺點
jvm能根據(jù)處理器性能適當(dāng)對機(jī)器指令進(jìn)行重排序,使機(jī)器指令能夠符合cpu的執(zhí)行特性,大限度的發(fā)揮機(jī)器性能。但是指令重排可以保證串行語義一致,但沒有義務(wù)保證多線程之間的語義也一致(即可能產(chǎn)生臟讀),簡單的說,兩行以上不相干的代碼在執(zhí)行的時候有可能先執(zhí)行的不是第一條,不見得是從上到 下順序執(zhí)行,執(zhí)行順序是可以被優(yōu)化的。
單線程環(huán)境里面確保程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果一致。
處理器在進(jìn)行重排序時必須考慮指令之間的數(shù)據(jù)依賴性。
多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在,兩個線程中使用的變量能否保證一致性是無法確定的,結(jié)果無法預(yù)測。
上述問題是不可以的,y和x變量都還沒有定義,也不符合屬于依賴性,y和x依賴1、2、3。
jmm規(guī)范下,多線程對變量的讀寫過程?在jmm中,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見性或者代碼重排序,那么這兩個操作之間必須存在happens-before 先發(fā)生原則。邏輯上的先后關(guān)系 例子如下圖
先行發(fā)生原則說明
如果java內(nèi)存模型中所有的有序操作都僅僅依靠volatile和synchronized來完成。那么有很多操作都將會變得非常啰嗦,但是我們在編寫java并發(fā)代碼的時候并沒有覺察這一點。
我們并沒有時時、處處、次次,添加volatile和synchronized來完成程序。這是因為java語言中jmm原則下有一個“先行發(fā)生”(happens-before)的原則限制和規(guī)矩,給立好了規(guī)矩:
這個原則非常重要:
它是判斷數(shù)據(jù)是否存在競爭,線程是否安全非常有效的手段,依賴這個原則,我們可以通過幾條簡單規(guī)則一攬子解決并發(fā)環(huán)境下兩個操作之間是否可能存在沖突的所有問題,而不需要陷入java內(nèi)存模型苦澀難懂的底層編譯原理中。
happens-before總原則
1 如果一個操作happens-before另外一個操作,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見,而且第一個操作的執(zhí)行順序排在第二個操作之前。
2 兩個操作之間存在happens-before關(guān)系,并不意味著一定要按照happens-before原則限定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果與按照happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序并不非法。
happens-before之8條規(guī)定
1次序規(guī)則
一個線程內(nèi),按照代碼順序,寫在前面的操作先行發(fā)生于寫在后面的操作。即 前一個操作的執(zhí)行結(jié)果可以被后續(xù)的操作獲取,講白點就是前面一個操作把變量x賦值為1,那么后面一個操作肯定能知道x已變成1.
2鎖定規(guī)則
一個unlock操作先行發(fā)生于后面對同一個鎖的lock操作。
3volatile變量規(guī)則
對一個volatile變量的寫操作先行發(fā)生于后面對這個變量的讀操作,前面的寫對后面的讀是可見的。? ? ? ??
4傳遞規(guī)則
如果操作A先行發(fā)生于B,B先行發(fā)生于C,那么A先行發(fā)生于C。
5線程啟動規(guī)則(Thread Start Rule)
thread對象的start()方法先行發(fā)生于此線程的每一個動作
6線程中斷規(guī)則(Thread Interruption Rule)
7線程終止規(guī)則(Thread Termination Rule)
8 對象的終結(jié)規(guī)則
happens-before小總結(jié)
在Java語言中 happens-before的語義本質(zhì)上是一種可見性
Ahappens-before B 意味著A發(fā)生過的事情對B來說是可見的,無論A事件和B事件是否發(fā)生在同一個線程中
修復(fù)方案
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前文章:java內(nèi)存模型之jmm-創(chuàng)新互聯(lián)
瀏覽地址:http://jinyejixie.com/article4/dpcjie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營銷型網(wǎng)站建設(shè)、網(wǎng)站改版、品牌網(wǎng)站設(shè)計、手機(jī)網(wǎng)站建設(shè)、網(wǎng)站設(shè)計公司、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容