国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java并發(fā)編程,3分分鐘深入分析volatile的實現(xiàn)原理

番茄西紅柿 / 1194人閱讀

摘要:一言以蔽之,被修飾的變量能夠保證每個線程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。為了實現(xiàn)內存語義時,編譯器在生成字節(jié)碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。

volatile原理volatile簡介

Java內存模型告訴我們,各個線程會將共享變量從主內存中拷貝到工作內存,然后執(zhí)行引擎會基于工作內存中的數(shù)據(jù)進行操作處理。 線程在工作內存進行操作后何時會寫到主內存中? 這個時機對普通變量是沒有規(guī)定的,而針對volatile修飾的變量給Java 虛擬機特殊的約定,線程對 volatile變量的修改會立刻被其他線程所感知,即不會出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象,從而保證數(shù)據(jù)的“可見性”。

一言以蔽之,被volatile修飾的變量能夠保證每個線程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象

volatile實現(xiàn)原理

volatile是怎樣實現(xiàn)了?比如一個很簡單的Java代碼:

instance = new Instancce() //instance是volatile變量

在生成匯編代碼時會在volatile修飾的共享變量進行寫操作的時候會多出Lock前綴的指令。 我們想這個Lock指令肯定有神奇的地方,那么Lock前綴的指令在多核處理器下會發(fā)現(xiàn)什么事情了?主要有這兩個方面的影響:

將當前處理器緩存行的數(shù)據(jù)寫回系統(tǒng)內存

這個寫回內存的操作會使得其他CPU里緩存了該內存地址的數(shù)據(jù)無效

為了提高處理速度,處理器不直接和內存進行通信,而是先將系統(tǒng)內存的數(shù)據(jù)讀到內部緩存(L1,L2或其他)后再進行操作,但操作完不知道何時會寫到內存。 如果對聲明了volatile的變量進行寫操作,JVM就會向處理器發(fā)送一條Lock前綴的指令,將這個變量所在緩存行的數(shù)據(jù)寫回到系統(tǒng)內存。但是,就算寫回到內存,如果其他處理器緩存的值還是舊的,再執(zhí)行計算操作就會有問題。

在多處理器下,為了保證各個處理器的緩存是一致的,就會實現(xiàn)緩存一致性協(xié)議,每個處理器通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己緩存的值是不是過期了,當處理器發(fā)現(xiàn)自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態(tài),當處理器對這個數(shù)據(jù)進行修改操作的時候,會重新從系統(tǒng)內存中把數(shù)據(jù)讀到處理器緩存里。 因此,經過分析我們可以得出如下結論:

Lock前綴的指令會引起處理器緩存寫回內存

一個處理器的緩存回寫到內存會導致其他工作內存中的緩存失效

當處理器發(fā)現(xiàn)本地緩存失效后,就會從主內存中重讀該變量數(shù)據(jù),即可以獲取當前最新值

這樣volatile變量通過這樣的機制就使得每個線程都能獲得該變量的最新值。

volatile的happens-before關系

happens-before中的volatile 變量規(guī)則(Volatile Variable Rule):對一個 volatile 變量的寫操作先行發(fā)生于后面對這個變量的讀操作。

public class VolatileExample {
    private int a = 0;
    private volatile boolean flag = false;
    public void writer(){
        a = 1;          //1
        flag = true;   //2
    }
    public void reader(){
        if(flag){      //3
            int i = a; //4
        }
    }
}

對應的happens-before關系如下:


加鎖線程A先執(zhí)行writer方法,然后線程B執(zhí)行reader方法。 圖中每一個箭頭兩個節(jié)點就代碼一個happens-before關系:

黑色的代表根據(jù)程序順序規(guī)則推導出來

紅色的是根據(jù)volatile變量的寫happens-before 于任意后續(xù)對volatile變量的讀

藍色的就是根據(jù)傳遞性規(guī)則推導出來的 這里的2 happen-before 3,同樣根據(jù)happens-before規(guī)則定義: 如果A happens-before B,則A的執(zhí)行結果對B可見,并且A的執(zhí)行順序先于B的執(zhí)行順序, 我們可以知道操作2執(zhí)行結果對操作3來說是可見的,也就是說當線程A將volatile變量 flag更改為true后線程B就能夠迅速感知。

volatile的內存語義
public class VolatileExample {
    private int a = 0;
    private volatile boolean flag = false;
    public void writer(){
        a = 1;          //1
        flag = true;   //2
    }
    public void reader(){
        if(flag){      //3
            int i = a; //4
        }
    }
}

假設線程A先執(zhí)行writer方法,線程B隨后執(zhí)行reader方法,初始時線程的本地內存中flag和a都是初始狀態(tài),下圖是線程A執(zhí)行volatile寫后的狀態(tài)圖:


當volatile變量寫后,線程B中本地內存中共享變量就會置為失效的狀態(tài),因此線程B需要從主內存中去讀取該變量的最新值。下圖就展示了線程B讀取同一個volatile變量的內存變化示意圖:


從橫向來看,線程A和線程B之間進行了一次通信,線程A在寫volatile變量時,實際上就像是給B發(fā)送了一個消息告訴線程B你現(xiàn)在的值都是舊的了,然后線程B讀這個volatile變量時就像是接收了線程A剛剛發(fā)送的消息。既然是舊的了,那線程B該怎么辦了?自然而然就只能去主內存去取啦。

volatile的內存語義實現(xiàn)

為了性能優(yōu)化,JMM在不改變正確語義的前提下,會允許編譯器和處理器對指令序列進行重排序,那如果想阻止重排序要怎么辦了? 答案是可以添加內存屏障

四類JMM內存屏障:


Java編譯器會在生成指令系列時在適當?shù)奈恢脮迦雰却嫫琳现噶顏斫固囟愋偷奶幚砥髦嘏判?/b>。 為了實現(xiàn)volatile的內存語義,JMM會限制特定類型的編譯器和處理器重排序,JMM會針對編譯器制定volatile重排序規(guī)則表:


"NO"表示禁止重排序。 為了實現(xiàn)volatile內存語義時,編譯器在生成字節(jié)碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。 對于編譯器來說,發(fā)現(xiàn)一個最優(yōu)布置來最小化插入屏障的總數(shù)幾乎是不可能的,為此,JMM采取了保守策略:

在每個volatile寫操作的前面插入一個StoreStore屏障

在每個volatile寫操作的后面插入一個StoreLoad屏障


在每個volatile讀操作的后面插入一個LoadLoad屏障

在每個volatile讀操作的后面插入一個LoadStore屏障


需要注意的是:volatile寫操作是在前面和后面分別插入內存屏障,而volatile讀操作是在后面插入兩個內存屏障。

volatile和synchronized的區(qū)別

    volatile本質是告訴JVM當前變量在寄存器(工作內存)中是無效的,需要去主內存重新讀取;synchronized是鎖定當前變量,只有持有鎖的線程才可以訪問該變量,其他線程都被阻塞直到該線程的變量操作完成;

    volatile僅僅能使用在變量級別;synchronized則可以使用在變量、方法和類級別;

    volatile僅僅能實現(xiàn)變量修改的可見性,不能保證原子性;而synchronized則可以保證變量修改的可見性和原子性;

    volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞;

    volatile修飾的變量不會被編譯器優(yōu)化;synchronized修飾的變量可以被編譯器優(yōu)化。



文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7107.html

相關文章

  • Java并發(fā)編程3分鐘深入分析volatile實現(xiàn)原理

    摘要:一言以蔽之,被修飾的變量能夠保證每個線程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。為了實現(xiàn)內存語義時,編譯器在生成字節(jié)碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。volatile原理volatile簡介Java內存模型告訴我們,各個線程會將共享變量從主內存中拷貝到工作內存,然后執(zhí)行引擎會基于工作內存中的數(shù)據(jù)進行操作處理。 線程在工作內存進行操作后何時會寫到主內存中...

    番茄西紅柿 評論0 收藏0
  • Java并發(fā)編程3分鐘深入分析volatile實現(xiàn)原理

    摘要:一言以蔽之,被修飾的變量能夠保證每個線程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。為了實現(xiàn)內存語義時,編譯器在生成字節(jié)碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。volatile原理volatile簡介Java內存模型告訴我們,各個線程會將共享變量從主內存中拷貝到工作內存,然后執(zhí)行引擎會基于工作內存中的數(shù)據(jù)進行操作處理。 線程在工作內存進行操作后何時會寫到主內存中...

    fanux 評論0 收藏0
  • 阿里之路+Java面經考點

    摘要:我的是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕源碼,三月份經歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕JDK源碼,三月份經歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...

    姘擱『 評論0 收藏0
  • volatile 用法及原理

    摘要:下面具體分析的用法及原理,涉及到內存模型可見性重排序以及偽共享等方面。緩存的使用提高了的運行效率,但是對于多核處理器會有一些問題。需要注意的是,用于保證一個變量的可見性,但是對于這種復合操作是無法保證原子性的。 簡介 在 Java 并發(fā)編程中,volatile 是經常用到的一個關鍵字,它可以用于保證不同的線程共享一個變量時每次都能獲取最新的值。volatile 具有鎖的部分功能并且性能...

    RdouTyping 評論0 收藏0
  • jvm原理

    摘要:在之前,它是一個備受爭議的關鍵字,因為在程序中使用它往往收集器理解和原理分析簡稱,是后提供的面向大內存區(qū)數(shù)到數(shù)多核系統(tǒng)的收集器,能夠實現(xiàn)軟停頓目標收集并且具有高吞吐量具有更可預測的停頓時間。 35 個 Java 代碼性能優(yōu)化總結 優(yōu)化代碼可以減小代碼的體積,提高代碼運行的效率。 從 JVM 內存模型談線程安全 小白哥帶你打通任督二脈 Java使用讀寫鎖替代同步鎖 應用情景 前一陣有個做...

    lufficc 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<