日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

聊聊高并发(三十六)Java内存模型那些事(四)理解Happens-before规则

發(fā)布時(shí)間:2024/1/17 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊高并发(三十六)Java内存模型那些事(四)理解Happens-before规则 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在前幾篇將Java內(nèi)存模型的那些事基本上把這個(gè)域底層的概念都解釋清楚了,聊聊高并發(fā)(三十五)Java內(nèi)存模型那些事(三)理解內(nèi)存屏障?這篇分析了在X86平臺(tái)下,volatile,synchronized, CAS操作都是基于Lock前綴的匯編指令來實(shí)現(xiàn)的,關(guān)于Lock指令有兩個(gè)要點(diǎn):

1. lock會(huì)鎖總線,總線是互斥的,所以lock后面的寫操作會(huì)寫入緩存和內(nèi)存,可以理解為在lock后面的寫緩存和寫內(nèi)存這兩個(gè)動(dòng)作稱為了一個(gè)原子操作。當(dāng)總線被鎖時(shí),其他的CPU是無(wú)法使用總線的,也就讓其他的讀寫都等待lock的釋放

2. Lock寫完后,發(fā)起它的CPU的緩存和內(nèi)存都是最新值,其他CPU相關(guān)的緩存行都會(huì)invalidate,后續(xù)的讀/寫就會(huì)發(fā)生緩存不命中,從內(nèi)存重新加載最新值。

?

這里有個(gè)隱含的點(diǎn),我沒找到具體的資料,但是按照很多資料的說法:?volatile的寫操作相當(dāng)于釋放鎖,volatile的讀操作相當(dāng)于進(jìn)入鎖可以做下面的推斷:

volatile操作的是一個(gè)變量,而鎖保護(hù)的程序段中涉及到的變量可以是多個(gè),既然兩者的效果是一樣的,那么很可能lock后面的寫會(huì)讓高速緩存/寫緩存區(qū)的所有臟數(shù)據(jù)都刷新回主存。只有這樣volatile在可見性方面和鎖保護(hù)的程序段的可見性才是行為一致的。

?

理解這個(gè)很重要,因?yàn)楹瓦@篇講的Happens-before傳遞性有關(guān)系。Happens-before剛看到的時(shí)候從語(yǔ)言上看很難理解,覺得是廢話,但是它實(shí)際描述的問題其實(shí)是可見性的問題,順帶著有一些由于防止重排序而帶來的有序性的問題。聊聊高并發(fā)(三十三)Java內(nèi)存模型那些事(一)從一致性(Consistency)的角度理解Java內(nèi)存模型?這篇說了,內(nèi)存模型是一致性這個(gè)問題域里面的,一致性問題只涉及到了可見性和有序性這兩種特性,不包含原子性,所以Happens-before實(shí)際上是一系列的一致性的約束,所以它涉及到了可見性和有序性的意思,但沒有原子性的含義。

?

happens-before俗解?這篇文章已經(jīng)寫的很清楚了,我這邊再結(jié)合上一篇內(nèi)存屏障的一些概念錦上添花一下,進(jìn)一步說明這個(gè)問題

?

下面這些Happens-before的規(guī)則是從JSR 133 (Java Memory Model) FAQ?摘出來的,一條條看

?

  • Each action in a thread happens before every action in that thread that comes later in the program's order.
  • 可以理解為對(duì)于單個(gè)線程來說,前面的寫操作對(duì)后面都是可見的,這里肯定有人問那指令重排序之后怎么保證這點(diǎn)呢,我也有這個(gè)疑問,所以我理解的是如果這個(gè)寫是同步的,那么對(duì)單線程來說,所有同步的寫都是按照program order的,這個(gè)也是順序一致性的第一層含義。要理解的是,Java在使用了同步手段之后,被同步保護(hù)的點(diǎn)都是保證順序一致性的。因?yàn)橥降牡讓訉?shí)現(xiàn)比如內(nèi)存屏障 / lock都有防止重排序的含義

?

?

  • An unlock on a monitor happens before every subsequent lock on?that same?monitor.
  • 可以理解為一個(gè)鎖的釋放后它前面的寫操作對(duì)后續(xù)進(jìn)入同一個(gè)鎖的線程可見,對(duì)鎖來說這個(gè)太肯定了,釋放時(shí)會(huì)lock cmpxchg一次,進(jìn)入時(shí)會(huì)lock cmpxchg一次,兩次都保證了可見性

?

  • A write to a volatile field happens before every subsequent read of?that same?volatile.
  • 可以理解為volatile的寫操作對(duì)后續(xù)的讀可見,也是lock addl操作保證了寫volatile的可見性

?

  • A call to?start()?on a thread happens before any actions in the started thread.
  • 可以理解為線程start()寫線程開始狀態(tài)對(duì)后續(xù)線程的其他動(dòng)作可見,JVM內(nèi)部處理了,實(shí)際實(shí)現(xiàn)肯定也是用了lock/內(nèi)存屏障來實(shí)現(xiàn)的,其實(shí)在聊聊JVM(九)理解進(jìn)入safepoint時(shí)如何讓Java線程全部阻塞?中我們提到了線程狀態(tài)的改變,在JVM里面是對(duì)一個(gè)線程狀態(tài)變量進(jìn)行原子的修改,這個(gè)狀態(tài)的改變是原子的,并且可見的,當(dāng)然就具備了Happens-before的能力

?

  • All actions in a thread happen before any other thread successfully returns from a?join()?on that thread.
  • 可以理解為一個(gè)被join的線程中所有的寫操作在它join結(jié)束后回到原來的線程時(shí),對(duì)原來的線程可見。這個(gè)和上面的原理差不多,就是JVM在修改線程狀態(tài)的時(shí)候是一次原子操作,并且保證了可見性(估計(jì)是一次CAS),所以連帶著修改狀態(tài)前面的修改也都對(duì)后續(xù)的操作可見了

?

其他還有一些Happens-before規(guī)則,比如CAS操作,原子變量的修改都有Happens-before的含義,另外Happens-before具備傳遞性,比如 A happens beofre B, B happens before C, 那么A肯定 happens before C。

為什么具備傳遞性呢,原因還是在開篇的時(shí)候說的,lock/內(nèi)存屏障不僅僅把當(dāng)前的地址的數(shù)據(jù)原子的寫到緩存和內(nèi)存,肯定也把這之前CPU緩存/write buffer的臟數(shù)據(jù)寫回到主內(nèi)存了,這樣就實(shí)現(xiàn)了Happens before的傳遞性。

?

所以所有用到volatile ,synchronized, CAS的地方都具備Happens before的傳遞性,顯式鎖和原子變量底層都是基于CAS來實(shí)現(xiàn)的,當(dāng)然用到它們的時(shí)候也具備了Happens before的傳遞性。

?

所以下面這個(gè)例子就很好理解了,比如?y是volatile變量或者是原子變量/同步器類等等用到CAS的

線程A ? ?? 線程B??

x = 1??????? a = y

y = 2 ? ? ?? b = x

?

如果在時(shí)間順序上y=2這個(gè)對(duì)被同步的變量的寫先發(fā)生于 a = y 這個(gè)對(duì)被同步的變量的讀,那么可以肯定的說 b = x = 1。

有人問 x = 1會(huì)不會(huì)被重排到 y =2 之后,答案是不會(huì),因?yàn)閥是個(gè)被同步的變量,防止重排序, x 不會(huì)跨越內(nèi)存屏障排到y(tǒng)=2之后,所以

b = x同樣也不會(huì)被重排序到 a = y前面,因?yàn)?y是被同步的變量,內(nèi)存屏障同樣不會(huì)讓屏障后面的操作跨越到前面去

?

所以只要 y =2 寫操作發(fā)生在 a = y讀操作之前,那么最后 x = 1 肯定先于 b=x,所以 b = 1

?

參考資料:

happens-before俗解

總結(jié)

以上是生活随笔為你收集整理的聊聊高并发(三十六)Java内存模型那些事(四)理解Happens-before规则的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。