中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

如何解析java多線程volatile內存語義

這期內容當中小編將會給大家?guī)碛嘘P如何解析java多線程volatile內存語義,文章內容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

專業(yè)網(wǎng)站制作公司,專做排名好的好網(wǎng)站,排在同行前面,為您帶來客戶和效益!成都創(chuàng)新互聯(lián)公司為您提供品質好成都網(wǎng)站建設,五站合一網(wǎng)站設計制作,服務好的網(wǎng)站設計公司,負責任的成都網(wǎng)站制作公司!

volatile關鍵字是java虛擬機提供的最輕量級額的同步機制。由于volatile關鍵字與java內存模型相關,因此,我們在介紹volatile關鍵字之前,對java內存模型進行更多的補充(之前的博文也曾介紹過)。

1. java內存模型(JMM)

JMM是一種規(guī)范,主要用于定義共享變量的訪問規(guī)則,目的是解決多個線程本地內存與共享內存的數(shù)據(jù)不一致、編譯器處理器的指令重排序造成的各種線程安全問題,以保障多線程編程的原子性、可見性和有序性。

JMM規(guī)定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存,線程中的工作內存中存儲了該線程用到的變量的主內存的拷貝,各線程對變量的所有操作都必須在工作內存中進行,線程之間的變量值的傳遞都必須通過主內存來進行。

JMM定義了8中操作實現(xiàn)主內存與工作內存的交互協(xié)議:

1)lock:作用于主內存,它把一個變量標識為一條線程的獨占狀態(tài)。    2)unlock:作用于主內存,它把一個處于鎖定狀態(tài)的變量的釋放出來?!   ?)read:作用于主內存,它把一個變量的值從主內存?zhèn)鬏數(shù)骄€程的工作內存中。    4)load:作用于工作內存,它把從主內存中read到的值放入工作內存的變量副本中?!   ?)use:作用于工作內存,它把一個變量的值從主內存?zhèn)鬟f給執(zhí)行引擎    6)assign:作用與工作內存,它把一個從執(zhí)行引擎接收到的值賦值給工作內存的變量?!   ?)store:作用于工作內存,把工作內存中一個變量的值傳送到主內存。    8)write:作用于主內存,它把store操作從工作內存中得到的值放入主內存中的變量中。

這8中操作以及對著8中操作的規(guī)則的限制就能確定哪些內存訪問在并發(fā)條件下是線程安全的,這種方式比較繁瑣,jdk1.5之后提出了提出了happens-before規(guī)則來判斷線程是否安全。

可以這么理解,happens-before規(guī)則是JMM的核心.Happens-before就是用來確定兩個操作的執(zhí)行順序。這兩個操作可在同一線程中,也可以在兩個線程中。

happens-before規(guī)定:如果一個操作happens-before另個一操作,那么第一個操作的結果對第二個操作可見(但這并不意味著處理器必須按照happens-before順序執(zhí)行,只要不改變執(zhí)行結果,可任意優(yōu)化)。happens-before規(guī)則已在前邊博文中介紹,這里不再重復(http://www.cnblogs.com/gdy1993/p/9117331.html)

JMM內存規(guī)則僅僅是一種規(guī)則,規(guī)則的最終落實是通過java虛擬機、編譯器以及處理器一同協(xié)作來落實的,而內存屏障是java虛擬機、編譯器、處理器之間溝通的紐帶。

而java原因封裝了這些底層的具體實現(xiàn)與控制,提供了synchronized、lock和volatile等關鍵字的來保障多線程安全問題。

2. volatile關鍵字

(1)volatile對可見性的保證

在介紹volatile關鍵字之前,先來看這樣一段代碼:

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

有兩個線程:線程1和線程2,線程1在stop==false時,不停的執(zhí)行doSomething()方法;線程2在執(zhí)行到一定情況時,將stop設置為true,將線程1中斷,很多人采用這種方式中斷線程,但這并不是安全的。因為stop作為一個普通變量,線程2對其的修改,并不能立刻被線程1所感知,即線程1對stop的修改僅僅在自己的工作內存中,還沒來的急寫入主內存,線程2工作內存中的stop并未修改,可能導致線程無法中斷,雖然這種可能性很小,但一旦發(fā)生,后果嚴重。

而使用volatile變量修飾就能避免這個問題,這也是volatile第一個重要含義:

volatile修飾的變量,能夠保證不同線程對這個變量操作的可見性,即一個線程修改了這個變量的值,這個新值對于其他線程是立即可見的。

volatile的對可見性保證的原理:

對于volatile修飾的變量,當某個線程對其進行修改時,會強制將該值刷新到主內存,這就使得其他線程對該變量在各自工作內存中的緩存無效,因而在其他線程對該變量進行操作時,必須從主內存中重新加載

(2)volatile對原子性的保障?

首先來看這樣一段代碼(深入理解java虛擬機):

public class VolatileTest {  public static volatile int race = 0;  public static void increase() {    race++;  }  public static final int THREAD_COUNT = 20;  public static void main(String[] args) {    Thread[] threads = new Thread[THREAD_COUNT];    for (Thread t : threads) {      t = new Thread(new Runnable() {        @Override        public void run() {          for(int i = 0; i < 10000; i++) {            increase();          }        }      });      t.start();    }    while(Thread.activeCount() > 1) {      Thread.yield();    }    System.out.println(race);//race < 200000  }}

race是volatile修飾的共享變量,創(chuàng)建20個線程對這個共享變量進行自增操作,每個線程自增的次數(shù)為10000次,如果volatile能夠保證原子性的話,最終race的結果肯定是200000。但結果不然,每次程序運行race'的值總是小于200000,這也側面證明了volatile并不能保證共享變量操作的原子性。原理如下:

線程1讀取了race的值,然后cp分配的時間片結束,線程2此時讀取了共享變量的值,并對race進行自增操作,并將操作后的值刷新到主內存,此時線程1已經(jīng)讀取了race的值,因此保留的依然是原來的值,此時這個值已是舊值,對race進行自增操作后刷新到主內存,因此主內存中的值也是舊值。這也是volatile僅僅能保障讀到的是相對新值的原因。

(3)volatile對有序性的保障

首先來看這樣一段代碼:

//線程1    boolean initialized = false;    context = loadContext();    initialized = true;    //線程2    while(!initialized) {      sleep();    }    doSomething(context);

線程2在initialized變量為true時,使用context變量完成一些操作;線程1負責加載context,并在加載完成后將initialized變量設為true。但是,由于initialized只是一個普通變量,普通變量僅僅能夠保證在該方法的執(zhí)行過程中,所有依賴賦值結果的地方都能獲得正確的值,而不能保證變量的賦值順序與程序代碼的執(zhí)行順序一致。因此就可能出現(xiàn)這樣一種情況,當線程1將initialized變量設為true時,context依然沒有加載完成,但線程2由于讀到initialized為true,就可能執(zhí)行了doSomething()方法,可能會產(chǎn)生非常奇怪的效果。

而volatile的第二個語義就是禁止重排序: 

寫volatile變量的操作與該操作之前的任何讀寫操作都不會被重排序;

讀volatile變量操作與該操作之后的任何讀寫操作都不會重排序。

(4) volatile的底層實現(xiàn)原理

java語言底層是通過內存屏障來實現(xiàn)volatile語義的。

對于volatile變量的寫操作:

①java虛擬機會在該操作之前插入一個釋放屏障(loadstore+storestore),釋放屏障禁止了volatile變量的寫操作與該操作之前的任何讀寫操作的重排序。

②java虛擬機會在該操作之后插入一個存儲屏障(storeload),存儲屏障使得對volatile變量的寫操作能夠同步到主內存。

對于volatile變量的讀操作:

③java虛擬機會在該操作之前插入一個loadload,使得每次對volatile變量的讀取都從主內存中重新加載(刷新處理器緩存)

④java虛擬機會在該操作之后插入一個獲得屏障(loadstore+loadload),使得volatile后的任何讀寫操作與該操作進行重排序。

①③保障可見性,②④保障有序性。

(5)volatile關鍵字與happens-before的關系

Happens-before規(guī)則中的volatile規(guī)則為:對于一個volatile域的寫happens-before后續(xù)每一個針對該變量的讀操作。

寫線程執(zhí)行write(),然后讀線程執(zhí)行read()方法,圖中每個箭頭都代表一個happens-before關系,黑色箭頭是根據(jù)程序順序規(guī)則,藍色箭頭根據(jù)volatile規(guī)則,紅色箭頭是根據(jù)傳遞性推出的,即操作2happens-before操作3,即對volatile共享變量的更新操作排在后續(xù)讀取操作之前,對volatile變量的修改對后續(xù)volatile變量的讀取可見。

上述就是小編為大家分享的如何解析java多線程volatile內存語義了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)頁名稱:如何解析java多線程volatile內存語義
URL標題:http://www.rwnh.cn/article10/gopsdo.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、品牌網(wǎng)站制作外貿建站、定制開發(fā)關鍵詞優(yōu)化、Google

廣告

聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

手機網(wǎng)站建設
汕尾市| 涟水县| 亚东县| 武威市| 瓮安县| 海盐县| 蒲江县| 罗源县| 汤阴县| 安龙县| 榆树市| 新巴尔虎右旗| 湾仔区| 淳安县| 武胜县| 东乌| 平定县| 台中市| 界首市| 永靖县| 沂源县| 贵定县| 邵武市| 新巴尔虎左旗| 库尔勒市| 松原市| 平安县| 建平县| 浦城县| 文水县| 建阳市| 忻州市| 叶城县| 北安市| 古交市| 靖远县| 东乌| 邳州市| 繁峙县| 门头沟区| 六枝特区|