一定要你明白Java中的volatile


今天Tony來和大家聊聊Java中關鍵字volatile

字節碼

首先volatile int a = 3;int a = 3;
加不加volatile關鍵字,最終生成的字節碼都一樣的。有興趣的同學可以試試看看字節碼是否一樣。

英文解釋

Adding volatile to the field does not change Java bytecode that reads or writes the field. It only changes the interpretation of the program by JVM or JIT compilation output if needed. It also influences optimizations.

中文理解

內存屏障的概念是針對CPU架構級別的,需要在JIT編譯器生成機器碼的時候才能看到。

java內存

講講java內存,在java內存中所有的變量都存在與主內存中,每個線程都有自己的工作內存。每個線程中的所用到的變量都是一個副本,都是從主內存中拷貝過來的。

在多線程的場景下,處於性能的考慮。每個線程處理變量的時候都會從主內存復制到當前的cpu緩存中,因為cpu緩存處理速度是相當的快。隨之帶來的問題就是一個變量被多個線程在不同的cpu中訪問。

不同線程之間不能直接訪問對方線程間的變量。他們之間的數據都傳遞都是通過主內存來實現的。

volatile關鍵字

java中用volatile 修飾的關鍵字將會標記在“主內存”中,每次對volatile變量的讀取都是從計算機的主內存讀取,不是從cpu緩存中讀取。可以這么理解每次我都是取主內存里的數據。

同樣對volatile變量的寫入同時會寫入到主內存和cpu緩存中。


不用volatile

舉個例子,當線程T1對變量a進行寫操作此刻a=1,如果此刻沒有使用
volatile 關鍵字,那么此刻線程T2中的a還是等於0。
這個時候線程T2並不能讀取到線程T1寫的a=1,此刻線程T1並沒有將a=1回寫到主內存中去。

線程的可見性,就是上面說的這種情況。1個線程對變量進行操作並不會通知到另一個線程。

使用volatile內存可見性

public class VolatileObject {
  public volatile int a = 0;
}

上面代碼當線程T1修改a的值,另一個線程T2讀取a的自值,
在上面給出的場景中,一個線程(T1)修改計數器,另一個線程(T2)讀取計數器(但從不修改),聲明計數器變量volatile足以保證T2對計數器變量的寫入可見。

指令重排序

1、重排序是指編譯器和處理器為了優化程序性能而對指令序列進行重新排序的一種手段。

2、調制執行語句的順序,重排序是為了更好的提升性能。

3、重排序保證在單線程下不會改變執行結果,但在多線程下可能會改變執行結果。

舉個例子🌰

int x=1;  //(1)
int y=2;  //(2)
int z=x+y;//(3)
  • JVM在單線程中不影響結果的情況下會對指令重排序

在上面的代碼中,先執行(1)(2)再執行(3)和先執行(2)(1)(3)對你最終結果都沒有什么影響,在JVM中居於優化有可能執行的是
(2)(1)(3)

Happens-Before

Happens-Before原則

happens-before原則是Java內存模型中定義的兩項操作之間的偏序關系,如果說操作A先行發生於操作B,其實就是說在發生操作B之前,操作A產生的影響能被操作B觀察到。“影響”包括修改了內存中共享變量的值、 發送了消息、 調用了方法等。

volatile機制

volatile 並不能保證原子性
想象一下,如果線程T1將一個值為0的共享計數器變量讀入其CPU緩存,則將其遞增為1,並沒有將更改后的值寫回主內存。

然后線程T2可以將相同的計數器變量從主內存(此刻變量值仍然為0)讀取到自己的CPU緩存中。然后線程2也可以將計數器增加到1,並且也不會將其寫回主內存。這種情況如下圖所示

這個時候並不能保證原子性的效果,線程T2並沒有讀取到最近的數據。

volatile的底層是使用內存屏障來保證有序性的。

內存屏障有兩個能力:

  • 就像一套柵欄分割前后的代碼,阻止柵欄前后的沒有數據依賴性的代碼進行指令重排序,保證程序在一定程度上的有序性。
  • 強制把寫緩沖區/高速緩存中的臟數據等寫回主內存,讓緩存中相應的數據失效,保證數據的可見性。

總結

  • 每個線程操作的都是自己工作內存中的數據,並發的情況下線程
    T1並沒有將最新修改的數據刷新到主內存中去,所以線程T2無法讀取到最新的數據。

  • volatile 不能保證原子性,不能保證原子性。

  • volatile 關鍵字修飾的代碼不會被重排序。

  • volatile 會在指令序列中插入內存屏障來禁止重排序。

題外話

最近市場環境不好,裁員的比比皆是。這是一個優勝劣汰的時代,那些缺乏競爭力的企業只會走向死亡,而員工缺乏自己的核心競爭力也無法在內部立足。所以我們對自己的未來要有方向有規划,大齡碼農不能再通過 跳槽溢價漲工資。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM