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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

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

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

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

1. lock會鎖總線,總線是互斥的,所以lock后面的寫操作會寫入緩存和內存,可以理解為在lock后面的寫緩存和寫內存這兩個動作稱為了一個原子操作。當總線被鎖時,其他的CPU是無法使用總線的,也就讓其他的讀寫都等待lock的釋放

2. Lock寫完后,發起它的CPU的緩存和內存都是最新值,其他CPU相關的緩存行都會invalidate,后續的讀/寫就會發生緩存不命中,從內存重新加載最新值。

?

這里有個隱含的點,我沒找到具體的資料,但是按照很多資料的說法:?volatile的寫操作相當于釋放鎖,volatile的讀操作相當于進入鎖可以做下面的推斷:

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

?

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

?

happens-before俗解?這篇文章已經寫的很清楚了,我這邊再結合上一篇內存屏障的一些概念錦上添花一下,進一步說明這個問題

?

下面這些Happens-before的規則是從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.
  • 可以理解為對于單個線程來說,前面的寫操作對后面都是可見的,這里肯定有人問那指令重排序之后怎么保證這點呢,我也有這個疑問,所以我理解的是如果這個寫是同步的,那么對單線程來說,所有同步的寫都是按照program order的,這個也是順序一致性的第一層含義。要理解的是,Java在使用了同步手段之后,被同步保護的點都是保證順序一致性的。因為同步的底層實現比如內存屏障 / lock都有防止重排序的含義

?

?

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

?

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

?

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

?

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

?

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

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

?

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

?

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

線程A ? ?? 線程B??

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

y = 2 ? ? ?? b = x

?

如果在時間順序上y=2這個對被同步的變量的寫先發生于 a = y 這個對被同步的變量的讀,那么可以肯定的說 b = x = 1。

有人問 x = 1會不會被重排到 y =2 之后,答案是不會,因為y是個被同步的變量,防止重排序, x 不會跨越內存屏障排到y=2之后,所以

b = x同樣也不會被重排序到 a = y前面,因為 y是被同步的變量,內存屏障同樣不會讓屏障后面的操作跨越到前面去

?

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

?

參考資料:

happens-before俗解

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。