當前位置:網站首頁>可見性,三面拼多多

可見性,三面拼多多

2021-08-19 23:40:24 程序員莎莎

上面這個緩存不一致的問題,我們先記下來,繼續來看Java內存模型,其實Java內存模型描述的上面講的計算機系統高速緩存和內存之間的關系類似。

Java內存模型描述了,各種變量的訪問規則,以及將變量存儲到內存和從內存讀取變量的這種底層細節。

在Java內存模型中關注的變量都是共享變量(實例變量、類變量)。
所有的共享變量都是存儲在主內存中的,但是每個線程在訪問變量的時候也都會在自己的工作內存處理器高速緩存)中保留一份共享變量的副本。

Java內存模型(Java Memory Model,簡稱JMM)規定:

線程對變量的所有操作(讀,寫)都必須在工作內存中進行,不能直接操作主內存中的數據。
不同線程之間 也不能直接訪問對方工作內存中的變量,線程間的變量值傳遞必須通過主內存進行中轉傳遞。
在JMM中工作內存和主內存的關系如下圖:

Volatile的可見性(保證立即可見)

繼續我們上面的緩存一致性的問題,這個問題,在Java內存模型中,就是可見性的問題,即一個線程修改了共享變量的值,對另一個線程來說是不是立即可見的。如果不是立即可見的,那麼就會出現緩存一致性的問題,如果是立即可見的,那麼另一個線程在進行操作的時候,拿到的變量值就是最新的。就可以解决可見性的問題。

那麼怎麼解决可見性問題呢?

  • 方案一:加鎖

將共享變量加鎖,無論是synchronized還是Lock都可以,加鎖達到的目的是在同一時間內只能有一個線程能對共享變量進行操作,就是說,共享變量從讀取到工作內存到更新值後,同步回主內存的過程中,其他線程是操作不了這個變量的。這樣自然就解决了可見性的問題了,但是這樣的效率比較低,操作不了共享變量的線程就只能阻塞。

  • 方案二:volatile修飾修飾共享變量

當一個共享變量被volatile修飾後,會保證每個線程將變量修改後的值立即同步回主內存中,當其他線程有需要讀取變量時會讀取到最新的變量值。

那麼volatile做了些什麼操作就能解决可見性的問題呢?

被volatile修飾的變量,在被線程操作時,會有這樣的機制:

     
  • 1.

就是線程對變量操作時會從主內存中讀取到自己的工作內存中,當線程對變量進行了修改後,那麼其他已經讀取了此變量的線程中的變量副本就會失效,這樣其他線程在使用變量的時候,發現已經失效,那麼就會去主內存中重新獲取,這樣獲取到的就只最新的值了。

那麼volatile這個關鍵字是如何實現這套機制的呢?

因為一臺計算機有多臺CPU,同一個變量,在多個CPU中緩存的值有可能不一樣,那麼以誰緩存的值為准呢?

既然大家都有自己的值,那麼各個CPU間就產生了一種協議,來保證按照一定的規律為准,來確定共享變量的准確值,這樣各個CPU在讀寫共享變量時都按照協議來操作。

這就是緩存一致性協議。

最著名的緩存一致性協議就是Intel的MESI了,說MESI時,先解釋一下,緩存行:

緩存行(cache line):CPU高速緩存的中可以分配的最小存儲單比特,高速緩存中的變量都是存在緩存行中的。

MESI的核心思想就是,當CPU對變量進行寫操作時發現,變量是共享變量,那麼就會通知其他CPU中將該變量的緩存行設置為無效狀態。當其他CPU在操作變量時發現此變量在的緩存行已經無效,那麼就會去主內存中重新讀取最新的變量。

  • 那麼其他CPU是如何發現變量被修改了的呢?

因為CPU和其他部件的進行通信是通過總線來進行的,所以每個CPU通過嗅探總線上的傳播數據,來檢查自己緩存的值是不是過期了,當處理器發現自己換成行對應的內存地址被修改後,就會將自己工作內存中的緩存行設置成無須狀態,當CPU對此變量進行修改時會重新從系統主內存中讀取變量。

Volatile的有序性(禁止指令重排)

一般來說,我們寫程序的時候,都是要把先代碼從上往下寫,默認的認為程序是自頂向下順序執行的,但是CPU為了提高效率,在保證最終結果准確的情况下,是會對指令進行重新排序的。就是說寫在前的代碼不一定先執行,在後面的也不一定晚執行。

舉個例子:

int a = 5; // 代碼1
int b = 8; // 代碼2
a = a + 4;	// 代碼3
int c = a + b;	// 代碼4


     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

上面四行代碼的執行順序有可能是

JMM在是允許指令重排序的,在保證最後結果正確的情况下,處理器可以盡情的發揮,提高執行效率。

當多個線程執行代碼的時候重排序的情况就更為突出了,各個CPU為了提高自己的效率,有可能會產生競爭情况,這樣就有可能導致最終執行的正確性。

所以為了保證在多個線程下最終執行的正確性,將變量用volatile進行修飾,這樣就會達到禁止指令重排序的效果(其實也可以通過加鎖,還有一些其他已知規則來實現禁止指令重排序,但是我們這裏只討論volatile的實現方式)。

那麼volatile是如何實現指令重排序的呢?

答案是:內存屏障

內存屏障是一組CPU指令,用於實現對內存操作的順序限制。
Java編譯器,會在生成指令系列時,在適當的比特置會插入內存屏障來禁止處理器對指令的重新排序。

volatile會在變量寫操作的前後加入兩個內存屏障,來保證前面的寫指令和後面的讀指令是有序的。

volatile在變量的讀操作後面插入兩個指令,禁止後面的讀指令和寫指令重排序。

有序性,不僅只有volatile能保證,其他的實現方式也能保證,但是如果每一種實現方式都要了解那對於開發人員來說就比較困難了。

所以從JDK5就出現了happen-before原則,也叫先行發生原則。
先行發生原則總結起來就是:如果一個操作A的產生的影響能被另一個操作B觀察到,那麼可以說,這個操作A先行發生與操作B。

這裏所說的影響包括內存中的變量的修改,調用了方法,發送量消息等。

<mark style=“margin: 0px; padding: 0.2em; box-sizing: border-box; background: rgb(252, 248, 227); color: rgb(0, 0, 0);”>volatile中的先行發生原則是,對一個volatile變量的寫操作,先行發生於後面任何地方對這個變量的讀操作。</mark>

Volatile無法保證原子性

原子性,是指一個操作過程要麼都成功,要麼都失敗,是一個獨立的完整的。

就像上面說的,如果多個線程對一個變量進行累加,那麼肯定得不到想要的結果,因為累加就不是一個原子操作。

要保證累加最終結果正確,要麼對累加變量加鎖,要麼就用AotomicInteger這樣的變量。

/**
 * 雙重檢查加鎖式單例


### 那麼如何才能正確的掌握Redis呢?

為了讓大家能够在Redis上能够加深,所以這次給大家准備了一些Redis的學習資料,還有一些大廠的面試題,包括以下這些面試題

*   並發編程面試題匯總

*   JVM面試題匯總

*   Netty常被問到的那些面試題匯總

*   Tomcat面試題整理匯總

*   Mysql面試題匯總

*   Spring源碼深度解析

*   Mybatis常見面試題匯總

*   Nginx那些面試題匯總

*   Zookeeper面試題匯總

*   RabbitMQ常見面試題匯總

JVM常頻面試:

![Redis高頻面試筆記:基礎+緩存雪崩+哨兵+集群+Reids場景設計](https://s2.51cto.com/images/20210819/1629387066205361.jpg)

Mysql面試題匯總(一)

![Redis高頻面試筆記:基礎+緩存雪崩+哨兵+集群+Reids場景設計](https://s2.51cto.com/images/20210819/1629387067894291.jpg)

Mysql面試題匯總(二)

![Redis高頻面試筆記:基礎+緩存雪崩+哨兵+集群+Reids場景設計](https://s2.51cto.com/images/20210819/1629387067528398.jpg)

Redis常見面試題匯總(300+題)

![Redis高頻面試筆記:基礎+緩存雪崩+哨兵+集群+Reids場景設計](https://s2.51cto.com/images/20210819/1629387068632495.jpg)



**有需要的朋友,可以[直接點擊這裏免費獲取](https://gitee.com/vip204888/java-p7)**

絕無套路!!
     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.

版權聲明
本文為[程序員莎莎]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2021/08/20210819234023641o.html

隨機推薦