摘要:禁止指令重排序優(yōu)化。只有當(dāng)線程對變量執(zhí)行的前一個動作是時,才能對執(zhí)行動作并且,只有當(dāng)對變量執(zhí)行的后一個動作是時,線程才能對變量執(zhí)行動作。變量不需要與其他的狀態(tài)變量共同參與不變約束。在某些情況下,的同步機制性要優(yōu)于鎖。
當(dāng)一個變量定義為volatile之后,它具備兩種特性:
保證此變量對所有線程的可見性,這里的“可見性”是指當(dāng)一條線程修改了這個變量的值,新值對于其他線程來說是可以立即得知的。
禁止指令重排序優(yōu)化。
在X86處理器下通過工具獲取 JIT編譯器生成的匯編指令來看下volatile變量進(jìn)行讀寫操作時CPU的行為:
Java 代碼如下:
// volatile Object instance; instance = new Singleton();
生成的匯編代碼如下:
0x01a3de1d: movb $0X0, 0X1104800(%esi); 0X01a3de24: lock addl $0X0, (%esp);
有volatile變量修飾的共享變量進(jìn)行寫操作的時候會多出第二行匯編代碼,Lock前綴的指令在多核處理器下會引發(fā)了兩件事件。
將當(dāng)前處理器緩存的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。
這個寫回內(nèi)存的操作會使在其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效。
再讓我們從Java內(nèi)存模型的角度分析下volatile變量。假定T表示一個線程,V和W分別表示兩個volatile變量,那么在進(jìn)行read, load, use, assign, store和write時需要滿足以下三條規(guī)則:
只有當(dāng)線程T對變量V執(zhí)行的前一個動作是load時,T才能對V執(zhí)行use; 并且,只有當(dāng)T對V執(zhí)行的后一個動作是use時,T才能對V執(zhí)行l(wèi)oad。T對V的use動作可以認(rèn)為是和線程T對V的load,read動作相關(guān)聯(lián),必須連續(xù)一起出現(xiàn)(這條規(guī)則要求 在工作內(nèi)存中,每次使用V前都必須先從主內(nèi)存刷新最新的值,用于保證能看見其他線程對變量V所做的修改后的值)。
只有當(dāng)線程T對變量V執(zhí)行的前一個動作是assign時,T才能對V執(zhí)行store動作;并且,只有當(dāng)T對變量V執(zhí)行的后一個動作是store時,線程T才能對變量V執(zhí)行assign動作。線程T對變量V的assign動作可認(rèn)為是和線程T對變量V的store, write動作相關(guān)聯(lián),必須連續(xù)一起出現(xiàn)(這條規(guī)則要求 在工作內(nèi)存中,每次修改V后都必須立刻同步回主內(nèi)存中,用于保證其他線程可以看到自己對變量V所做的修改)。
假定動作A是線程T對變量V實施的use或assign操作,假定動作F是和動作A相關(guān)聯(lián)的load或store動作,假定動作P是和動作F相應(yīng)的變量V的read或write動作;類似的,假定動作B是線程T對變量W實施的use或assign動作,假定動作G是和動作B相關(guān)聯(lián)的load或store動作,假定動作Q是和動作G相應(yīng)的變量W的read或write動作。如果A先于B,那P先于Q(這條規(guī)則要求 volatile修飾的變量不會被指令重排序優(yōu)化,保證代碼的執(zhí)行順序與程序的順序相同)。
由于volatile變量只能保證可見性,在不符合以下兩條規(guī)則的運算場景中,仍然要通過加鎖(使用synchronized或java.util.concurrent中的原子類)來保證原子性:
運行結(jié)果并不依賴變量的當(dāng)前值,或者能夠確保只有單一的線程修改變量的值。
變量不需要與其他的狀態(tài)變量共同參與不變約束。
在某些情況下,volatile的同步機制性要優(yōu)于鎖。并且,volatile變量讀操作的性能消耗與普通變量幾乎沒有什么差別,但是寫操作則可能會慢一些,因為它需要在本地代碼中插入許多內(nèi)存屏障指令來保證處理器不發(fā)生亂序執(zhí)行。
參考:
深入分析Volatile的實現(xiàn)原理
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66049.html
摘要:內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性可見性和有序性這個特征來建立的,我們來看下哪些操作實現(xiàn)了這個特性??梢娦钥梢娦允侵府?dāng)一個線程修改了共享變量的值,其他線程能夠立即得知這個修改。 Java內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性、可見性和有序性這3個特征來建立的,我們來看下哪些操作實現(xiàn)了這3個特性。 原子性(atomicity): 由Java內(nèi)存模型來直接保證原子性變量操作包括...
摘要:并發(fā)需要解決的問題功能性問題線程同步面臨兩個問題,想象下有兩個線程在協(xié)作工作完成某項任務(wù)。鎖可用于規(guī)定一個臨界區(qū),同一時間臨界區(qū)內(nèi)僅能由一個線程訪問。并發(fā)的數(shù)據(jù)結(jié)構(gòu)線程安全的容器,如等。 并發(fā)指在宏觀上的同一時間內(nèi)同時執(zhí)行多個任務(wù)。為了滿足這一需求,現(xiàn)代的操作系統(tǒng)都抽象出 線程 的概念,供上層應(yīng)用使用。 這篇博文不打算詳細(xì)展開分析,而是對java并發(fā)中的概念和工具做一個梳理。沿著并發(fā)模...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)。因為我心理很清楚,我的目標(biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學(xué)習(xí)計劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...
摘要:三關(guān)鍵字能保證原子性嗎并發(fā)編程藝術(shù)這本書上說保證但是在自增操作非原子操作上不保證,多線程編程核心藝術(shù)這本書說不保證。多線程訪問關(guān)鍵字不會發(fā)生阻塞,而關(guān)鍵字可能會發(fā)生阻塞關(guān)鍵字能保證數(shù)據(jù)的可見性,但不能保證數(shù)據(jù)的原子性。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchroniz...
摘要:并發(fā)編程的挑戰(zhàn)并發(fā)編程的目的是為了讓程序運行的更快,但是,并不是啟動更多的線程就能讓程序最大限度的并發(fā)執(zhí)行。的實現(xiàn)原理與應(yīng)用在多線程并發(fā)編程中一直是元老級角色,很多人都會稱呼它為重量級鎖。 并發(fā)編程的挑戰(zhàn) 并發(fā)編程的目的是為了讓程序運行的更快,但是,并不是啟動更多的線程就能讓程序最大限度的并發(fā)執(zhí)行。如果希望通過多線程執(zhí)行任務(wù)讓程序運行的更快,會面臨非常多的挑戰(zhàn):(1)上下文切換(2)死...
閱讀 2918·2021-10-19 10:09
閱讀 3136·2021-10-09 09:41
閱讀 3384·2021-09-26 09:47
閱讀 2697·2019-08-30 15:56
閱讀 602·2019-08-29 17:04
閱讀 992·2019-08-26 11:58
閱讀 2511·2019-08-26 11:51
閱讀 3362·2019-08-26 11:29