日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

Java并發(fā)的原子性、可見性、有序性

 

原文:https://www.jianshu.com/p/9c5a7d21c02f

內(nèi)存模型與運(yùn)行時(shí)數(shù)據(jù)區(qū)

內(nèi)存模型

JAVA內(nèi)存模型簡(jiǎn)稱JMM(Java Memory Model ),定義了程序中各個(gè)共享變量的訪問規(guī)則。

Java并發(fā)的原子性、可見性、有序性

 

Java Memory Model

變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程擁有自己的工作內(nèi)存用來存放變量的拷貝,線程的讀寫操作是在各自的工作內(nèi)存中進(jìn)行的,操作的對(duì)象都是變量的拷貝,操作完畢后在刷新到主內(nèi)存。

JMM規(guī)范定義了工作內(nèi)存和主內(nèi)存之間變量訪問的細(xì)節(jié),通過保障原子性、有序性、可見性實(shí)現(xiàn)線程安全。

運(yùn)行時(shí)數(shù)據(jù)區(qū)

運(yùn)行時(shí)數(shù)據(jù)區(qū)(JVM Runtime Data Areas)定義了JVM運(yùn)行期內(nèi)存的管理劃分。

Java并發(fā)的原子性、可見性、有序性

 

JVM Runtime Data Areas

JVM在運(yùn)行時(shí)把內(nèi)存劃分成多個(gè)功能區(qū),每個(gè)區(qū)域?qū)?yīng)著不能的存儲(chǔ)內(nèi)容,生命周期,共享性質(zhì),GC策略等。

可以看到,能被線程共享的是方法區(qū)和堆中的數(shù)據(jù),也就是實(shí)例對(duì)象、數(shù)組和靜態(tài)變量,這些共享數(shù)據(jù)受到JMM規(guī)范影響。

而局部變量、方法參數(shù)、異常處理參數(shù)都在虛擬機(jī)棧中,這些數(shù)據(jù)為線程私有的,所以不受JMM規(guī)范影響。

原子性、可見性、有序性

原子性

原子操作是指一個(gè)操作不會(huì)被線程調(diào)度機(jī)制打斷,一旦開始,就一直運(yùn)行到結(jié)束,中間不會(huì)有任何線程切換(context switch)。

原子性可以保障讀取到的某個(gè)屬性的值是由一個(gè)線程寫入的。 變量不會(huì)在同一時(shí)刻受到多個(gè)線程同時(shí)寫入造成干擾。如在32位的JVM中對(duì)64位long 或double值的寫操作是分成兩次相鄰的32位值寫操作,在多線程的環(huán)境下,可能會(huì)有線程只讀到了前32位,這種操作就是非原子性的,非原子性操作會(huì)受到多線程的干擾而產(chǎn)生結(jié)果混亂。

基本類型的單次讀寫操作是原子的,但是復(fù)合操作如:int i=0;i++,就是非原子性的。

JMM保障原子性的方法:volatile語(yǔ)義(保證變量單次操作的的原子性)、鎖語(yǔ)義。

可見性

Java并發(fā)的原子性、可見性、有序性

 

共享內(nèi)存模型

可見性是指一個(gè)線程對(duì)變量的值進(jìn)行了修改,其他線程能夠立即得知這個(gè)修改。

如上圖:在共享內(nèi)存模型中如果有一個(gè)線程對(duì)變量i進(jìn)行了修改,在沒有可見性保障的情況下,其他兩個(gè)線程看到的i的值都是不確定的,變量i在數(shù)據(jù)爭(zhēng)用的情況下不具備不可見性。

可見性是保障多線程操作中數(shù)據(jù)一致性和結(jié)果正確性的基石,多線程環(huán)境下影響變量可見性的因素:

1、 指令重排序

2、 線程調(diào)度(切換)

3、 工作內(nèi)存和主內(nèi)存沒有及時(shí)刷新

JMM保障可見性的方法:fianl語(yǔ)義、volatile語(yǔ)義、鎖語(yǔ)義。

有序性

現(xiàn)代CPU的計(jì)算速度遠(yuǎn)遠(yuǎn)高于內(nèi)存的讀寫速度,CPU會(huì)采用高速緩存來抵消內(nèi)存訪問帶來的延遲。甚至高速緩存也分成多級(jí),最快的離CPU最近,但是其存取速度還是遠(yuǎn)遠(yuǎn)低于CUP指令執(zhí)行的速度,為了減少CACHE_WAIT,CPU會(huì)采用指令級(jí)并行重排序來提供執(zhí)行效率,也可以叫做CPU亂序執(zhí)行。

CUP的高速緩存與內(nèi)存之間不是實(shí)時(shí)同步的,高速緩與高速緩間也不是實(shí)時(shí)同步,而是通過緩存一致性協(xié)議(MESI)將數(shù)據(jù)新到主內(nèi)存,緩存和讀寫緩沖區(qū)之間也會(huì)通過指令重排序來優(yōu)化數(shù)據(jù)的刷新。

JIT編譯器也會(huì)在代碼編譯的時(shí)候?qū)Υa進(jìn)行重新整理,最大限度的去優(yōu)化代碼的執(zhí)行效率。

所以一段JAVA代碼從執(zhí)行到獲得結(jié)果,其執(zhí)行的順序其實(shí)是經(jīng)歷了2個(gè)階段三種重排序的優(yōu)化:

Java并發(fā)的原子性、可見性、有序性

 

代碼重排序過程

保障重排序后結(jié)果正確性

1、as-if-serial語(yǔ)義

as-if-serial語(yǔ)義的意思指:所有的指令都可以為了優(yōu)化而被重排序,但是必須保證最終執(zhí)行的結(jié)果和重排序之前的結(jié)果是一致的,編譯器和處理器都會(huì)保證單線程下的as-if-serial語(yǔ)義。主要遵守的規(guī)則是重排序不破壞數(shù)據(jù)的依賴關(guān)系,如下圖,指令C依賴指令A(yù)和指令B,那么重排序只能在指令A(yù)和指令B之間發(fā)生。

Java并發(fā)的原子性、可見性、有序性

 

數(shù)據(jù)依賴關(guān)系

as-if-serial語(yǔ)義保證了單線程環(huán)境下重排序之后程序執(zhí)行結(jié)果的正確性,JVM在單線程的情況下會(huì)遵as-if-serial語(yǔ)義,無(wú)需擔(dān)心重排序會(huì)干擾心內(nèi)存可見性。

2、hAppens-before原則

Java并發(fā)的原子性、可見性、有序性

 

示例1

按照寫代碼的主觀意愿,可能期望是要么指令1先執(zhí)行,要么指令3先執(zhí)行,指令1先執(zhí)行就不應(yīng)該看到到指令4寫入的值,如果是指令3先執(zhí)行,就不應(yīng)該看到指令2寫入的值。

如果編譯器或者執(zhí)行CPU進(jìn)行了重排序,指令4在指令1前先執(zhí)行了,指令2在指令3之前執(zhí)行了,就會(huì)出現(xiàn)r2 == 2和r1 == 1這種有違直覺的結(jié)果。然而,從單個(gè)線程的角度,指令1和指令2重排序是遵循as-if-serial語(yǔ)義的,不會(huì)影響該線程獲得正確的結(jié)果。但是,從多線程的角度看,編譯器或者指令重排序影響到了代碼原本想要表達(dá)語(yǔ)義。

Java并發(fā)的原子性、可見性、有序性

 

示例2

這個(gè)示例中指令1和指令2之間沒有依賴關(guān)系遵循as-if-serial語(yǔ)義重排序,對(duì)單線程執(zhí)行結(jié)果的正確性沒有影響,但是多線程環(huán)境下,如果thread1執(zhí)行完指令1,thread2執(zhí)行,那i的值會(huì)出現(xiàn)有背預(yù)期的情況,因?yàn)閠hread1中對(duì)共享變量a的修改,對(duì)thread2是不可見的。

基于數(shù)據(jù)依賴性的as-if-serial語(yǔ)義無(wú)法保證多線程環(huán)境下,重排序之后程序執(zhí)行結(jié)果的正確性。JMM中happens-before原則就是用來保障多線程環(huán)境下變量可見性的。

先行發(fā)生原則( happens-before )是JMM用來規(guī)定兩個(gè)操作之間的偏序關(guān)系,這兩個(gè)操作是可以跨線程的。happens-before中確定了8條規(guī)則,如果如果兩個(gè)操作之間的關(guān)系可以從下列規(guī)則推導(dǎo)出來說明兩個(gè)操作是有序的。

happens-before并不限定指令重排序,如果如果重排序之后的執(zhí)行結(jié)果與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么JVM允許這種重排序。happens-before原則保證了前后兩個(gè)操作間不會(huì)被重排序且后者對(duì)前者的內(nèi)存是可見的。

happens-before八條規(guī)則:

1、程序次序規(guī)則:一個(gè)線程中的每個(gè)操作,happens-before于該線程中的任意后續(xù)操作(一個(gè)線程內(nèi)保證語(yǔ)義的串行性)。

2、鎖定規(guī)則:對(duì)一個(gè)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖。

3、volatile變量規(guī)則:volatile變量的寫操作happens-before于后面對(duì)這個(gè)變量的讀操作。

4、傳遞規(guī)則:如果A happens-before B且Bhappens-before C,那么A happens-before C。

5、線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法happens-before于此線程的每個(gè)一動(dòng)作。

6、線程中斷規(guī)則:對(duì)線程interrupt()方法的調(diào)用happens-before于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生。

7、線程終結(jié)規(guī)則:線程中所有的操作都happens-before于線程的終止。

8、對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成happens-before于他的finalize()方法的開始。

小結(jié)

1、JMM規(guī)范定義了工作內(nèi)存和主內(nèi)存之間變量訪問的細(xì)節(jié),通過保障原子性、有序性、可見性實(shí)現(xiàn)線程安全。

2、線程調(diào)度(切換)會(huì)影響數(shù)據(jù)操作的原子性,JMM通過fianl語(yǔ)義、volatile語(yǔ)義、鎖語(yǔ)義來保障原子性。

3、線程調(diào)度(切換)、指令重排序、內(nèi)存刷新都會(huì)影響可見性,JMM通過volatile語(yǔ)義、鎖語(yǔ)義來保障可見性。

4、內(nèi)存系統(tǒng)重排序、指令級(jí)并行重排序、編譯器優(yōu)化重排序都會(huì)影響到程序執(zhí)行的有序性,JMM通過happens-before原則保障并發(fā)環(huán)境下程序執(zhí)行的有序性。

分享到:
標(biāo)簽:Java
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定