中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM內存模型、可見性、指令重排序

發布時間:2021-09-03 18:40:17 來源:億速云 閱讀:154 作者:chen 欄目:大數據

本篇內容主要講解“JVM內存模型、可見性、指令重排序”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JVM內存模型、可見性、指令重排序”吧!


內存模型

    首先我們思考一下一個java線程要向另外一個線程進行通信,應該怎么做,我們再把需求明確一點,一個java線程對一個變量的更新怎么通知到另外一個線程呢?我們知道java當中的實例對象、數組元素都放在java堆中,java堆是線程共享的。(我們這里把java堆稱為主內存),而每一個線程都是自己私有的內存空間(稱為工作內存),如果線程1要向線程2通信,一定會經過類似的流程:

JVM內存模型、可見性、指令重排序

1、 線程1將自己工作內存中的X更新為1并刷新到主內存中;

2、 線程2從主內存讀取變量X=1,更新到自己的工作內存中,從而線程2讀取的X就是線程1更新后的值。

從上面的流程看出線程之間的通信都需要經過主內存,而主內存與工作內存的交互,則需要Java內存模型(JMM)來管理器。下圖演示了JMM如何管理主內存和工作內存:

JVM內存模型、可見性、指令重排序

當線程1需要將一個更新后的變量值刷新到主內存中時,需要經過兩個步驟:

1、 工作內存執行store操作;

2、 主內存執行write操作;

完成這兩步即可將工作內存中的變量值刷新到主內存,即線程1工作內存和主內存的變量值保持一致;

當線程2需要從主內存中讀取變量的最新值時,同樣需要經過兩個步驟:

1、主內存執行read操作,將變量值從主內存中讀取出來;

2、工作內存執行load操作,將讀取出來的變量值更新到本地內存的副本;

完成這兩步,線程2的變量和主內存的變量值就保持一致了。

可見性

    Java中有一個關鍵字volatile,它有什么用呢?這個答案其實就在上述java線程間通信機制中,我們想象一下,由于工作內存這個中間層的出現,線程1和線程2必然存在延遲的問題,例如線程1在工作內存中更新了變量,但還沒刷新到主內存,而此時線程2獲取到的變量值就是未更新的變量值,又或者線程1成功將變量更新到主內存,但線程2依然使用自己工作內存中的變量值,同樣會出問題。不管出現哪種情況都可能導致線程間的通信不能達到預期的目的。例如以下例子:

//線程1 boolean stop = false; while(!stop){ doSomething(); } //線程2

stop

= true;

這個經典的例子表示線程2通過修改stop的值,控制線程1中斷,但在真實環境中可能會出現意想不到的結果,線程2在執行之后,線程1并沒有立刻中斷甚至一直不會中斷。出現這種現象的原因就是線程2對線程1的變量更新無法第一時間獲取到。

但這一切等到Volatile出現后,再也不是問題,Volatile保證兩件事:

1、 線程1工作內存中的變量更新會強制立即寫入到主內存;

2、 線程2工作內存中的變量會強制立即失效,這使得線程2必須去主內存中獲取最新的變量值。

所以這就理解了Volatile保證了變量的可見性,因為線程1對變量的修改能第一時間讓線程2可見。

指令重排序

關于指令排序我們先看一段代碼:

int a = 0;?boolean flag = false;

//線程1

public void writer() {

a = 1;

flag = true;

}

//線程2

public void reader() {

if (flag) {

int i= a+1;

...... }

}

線程1依次執行a=1,flag=true;線程2判斷到flag==true后,設置i=a+1,根據代碼語義,我們可能會推斷此時i的值等于2,因為線程2在判斷flag==true時,線程1已經執行了a=1;所以i的值等于a+1=1+1=2;但真實情況卻不一定如此,引起這個問題的原因是線程1內部的兩條語句a=1;flag=true;可能被重新排序執行,如圖:

JVM內存模型、可見性、指令重排序

這就是指令重排序的簡單演示,兩個賦值語句盡管他們的代碼順序是一前一后,但真正執行時卻不一定按照代碼順序執行。你可能會說,有這個指令重排序那不是亂套了嗎?我寫的程序都不按我的代碼流程走,這怎么玩?這個你可以放心,你的程序不會亂套,因為java和CPU、內存之間都有一套嚴格的指令重排序規則,哪些可以重排,哪些不能重排都有規矩的。下列流程演示了一個java程序從編譯到執行會經歷哪些重排序:

JVM內存模型、可見性、指令重排序

在這個流程中第一步屬于編譯器重排查,編譯器重排序會按JMM的規范嚴格進行,換言之編譯器重排序一般不會對程序的正確邏輯造成影響。第二、三步屬于處理器重排序,處理器重排序JMM就不好管了,怎么辦呢?它會要求java編譯器在生成指令時加入內存屏障,內存屏障是什么?你可以理解為一個不透風的保護罩,把不能重排序的java指令保護起來,那么處理器在遇到內存屏障保護的指令時就不會對它進行重排序了。關于在哪些地方該加入內存屏障,內存屏障有哪些種類,各有什么作用,這些知識點這里就不再闡述了。可以參考JVM規范相關資料。

下面介紹一下在同一個線程中,不會被重排序的邏輯:

JVM內存模型、可見性、指令重排序

這三種情況中,任意改變一個代碼的順序,結果都會大不相同,對于這樣的邏輯代碼,是不會被重排序的。注意這是指單線程中不會被重排序,如果在多線程環境下,還是會產生邏輯問題,例如我們一開始舉的例子。

到此,相信大家對“JVM內存模型、可見性、指令重排序”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

jvm
AI

新兴县| 大丰市| 休宁县| 兴山县| 汤阴县| 孟村| 郑州市| 辽阳市| 双柏县| 广元市| 衡水市| 恩平市| 公主岭市| 镇安县| 灯塔市| 永城市| 达尔| 大城县| 乌拉特中旗| 天气| 来安县| 绍兴县| 阳高县| 交口县| 容城县| 家居| 龙里县| 都江堰市| 常宁市| 广丰县| 上饶市| 靖州| 隆昌县| 凤凰县| 鸡泽县| 泾川县| 无棣县| 勐海县| 永安市| 象州县| 广平县|