摘要:如何擁有可見性先介紹一下內(nèi)存模型中定義的種工作內(nèi)存與主內(nèi)存之間的原子操作鎖定作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占的狀態(tài)。并沒(méi)有達(dá)到期望的。
前言
要學(xué)習(xí)好Java的多線程,就一定得對(duì)volatile關(guān)鍵字的作用機(jī)制了熟于胸。最近博主看了大量關(guān)于volatile的相關(guān)博客,對(duì)其有了一點(diǎn)初步的理解和認(rèn)識(shí),下面通過(guò)自己的話敘述整理一遍。
有什么用?volatile主要對(duì)所修飾的變量提供兩個(gè)功能
可見性
防止指令重排序
本篇博客主要對(duì)volatile可見性進(jìn)行探討,以后發(fā)表關(guān)于指令重排序的博文。
一圖勝千言
上圖已經(jīng)把JAVA內(nèi)存模型(JMM)展示得很詳細(xì)了,簡(jiǎn)單概括一下
每個(gè)Thread有一個(gè)屬于自己的工作內(nèi)存(可以理解為每個(gè)廚師有一個(gè)屬于自己的鐵鍋)
所有Thread共用一個(gè)主內(nèi)存(餐廳所有的廚師共用同一個(gè)冰箱)
每個(gè)Thread操作數(shù)據(jù)之前都會(huì)去主內(nèi)存中獲取數(shù)據(jù)(廚師炒菜之前都要去冰箱里拿食材)
Thread:廚師
工作內(nèi)存:鐵鍋
store&load:放熟食,取食材
主內(nèi)存:冰箱
讀者可思考以下情景:
餐廳來(lái)了一位顧客點(diǎn)了一份紅燒肉,此時(shí)有兩位大廚(假設(shè)大廚之間互不通信),由于互不通信,所以兩位大廚都打開冰箱取出食材開始炒菜。
最后炒出了兩份紅燒肉,顧客只要一份。為什么會(huì)造成這種結(jié)果?
將此情景放在JAVA中即是:
線程A從主內(nèi)存中取了一個(gè)變量到工作內(nèi)存中,操作完畢后沒(méi)有及時(shí)放回主內(nèi)存中,于是線程B去取這個(gè)變量已經(jīng)過(guò)期了,取的是線程A操作之前的變量。
先介紹一下Java內(nèi)存模型中定義的8種工作內(nèi)存與主內(nèi)存之間的原子操作
lock( 鎖定 ):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占的狀態(tài)。
unlock(解鎖):作用于主內(nèi)存的變量,把一個(gè)處于鎖定的變量釋放出來(lái),釋放變量才可以被其他線程鎖定。
read(讀取):作用于主內(nèi)存的變量,把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動(dòng)作使用。
load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中。
use(使用):作用于工作內(nèi)存種的變量,它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作。
assign(賦值):作用于工作內(nèi)存中的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作。
store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write操作使用
write(寫入):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的值放入主內(nèi)存的變量中。
當(dāng)線程1對(duì)主內(nèi)存對(duì)象發(fā)起read操作到write操作套流程的時(shí)間里,線程2隨時(shí)都有可能對(duì)這個(gè)主內(nèi)存對(duì)象發(fā)起第二套操作
有什么危害呢?
假設(shè)主內(nèi)存中有一個(gè)
int a=0;
線程1和線程2分別執(zhí)行一次,理想狀態(tài)下最終a的值為2.
a++;
線程1在執(zhí)行了assign操作之后變量a的真實(shí)值已經(jīng)從0變成了1,但是這個(gè)過(guò)程發(fā)生在工作內(nèi)存中對(duì)其他線程不可見,若線程2此時(shí)對(duì)變量a的操作,讀取到的值仍然為0,因?yàn)闆](méi)有可見性,線程2的操作也僅僅是重復(fù)了線程1的操作,再次讓a從0變成了1。并沒(méi)有達(dá)到期望的a=2。
volatile變量對(duì)對(duì)象的操作更嚴(yán)格:
use之前不能被read&load
assign之后必須緊跟store&write
也就是說(shuō) read-load-use 和 assign-store-write成為了兩個(gè)不可分割的原子操作
盡管這時(shí)候在use和assign之間依然有一段真空期,有可能變量會(huì)被其他線程讀取,但是無(wú)論在哪一個(gè)時(shí)間點(diǎn)主內(nèi)存的變量和任一工作內(nèi)存的變量的值都是相等的。這個(gè)特性就導(dǎo)致了volatile變量不適合參與到依賴當(dāng)前值的運(yùn)算,如自增。
那么依靠可見性的特點(diǎn)volatile可以用在哪些地方呢?
《Java虛擬機(jī)》提到:
運(yùn)算結(jié)果并不依賴變量的當(dāng)前值(即結(jié)果對(duì)產(chǎn)生中間結(jié)果不依賴),或者能夠確保只有單一的線程修改變量的值
通常volatile用做保存某個(gè)狀態(tài)的boolean值。
部分參考自
volatile變量與普通變量的區(qū)別
<<深入理解Java虛擬機(jī) 高級(jí)特性與最佳實(shí)踐>>
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69423.html
摘要:假設(shè)不發(fā)生編譯器重排和指令重排,線程修改了的值,但是修改以后,的值可能還沒(méi)有寫回到主存中,那么線程得到就是很自然的事了。同理,線程對(duì)于的賦值操作也可能沒(méi)有及時(shí)刷新到主存中。線程的最后操作與線程發(fā)現(xiàn)線程已經(jīng)結(jié)束同步。 很久沒(méi)更新文章了,對(duì)隔三差五過(guò)來(lái)刷更新的讀者說(shuō)聲抱歉。 關(guān)于 Java 并發(fā)也算是寫了好幾篇文章了,本文將介紹一些比較基礎(chǔ)的內(nèi)容,注意,閱讀本文需要一定的并發(fā)基礎(chǔ)。 本文的...
摘要:是需要我們?nèi)ヌ幚砗芏嗍虑?,為了防止多線程給我們帶來(lái)的安全和性能的問(wèn)題下面就來(lái)簡(jiǎn)單總結(jié)一下我們需要哪些知識(shí)點(diǎn)來(lái)解決多線程遇到的問(wèn)題。 前言 不小心就鴿了幾天沒(méi)有更新了,這個(gè)星期回家咯。在學(xué)校的日子要努力一點(diǎn)才行! 只有光頭才能變強(qiáng) 回顧前面: 多線程三分鐘就可以入個(gè)門了! Thread源碼剖析 本文章的知識(shí)主要參考《Java并發(fā)編程實(shí)戰(zhàn)》這本書的前4章,這本書的前4章都是講解并發(fā)的基...
摘要:內(nèi)存模型是圍繞著在并發(fā)過(guò)程中如何處理原子性可見性和有序性這個(gè)特征來(lái)建立的,我們來(lái)看下哪些操作實(shí)現(xiàn)了這個(gè)特性??梢娦钥梢娦允侵府?dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改。 Java內(nèi)存模型是圍繞著在并發(fā)過(guò)程中如何處理原子性、可見性和有序性這3個(gè)特征來(lái)建立的,我們來(lái)看下哪些操作實(shí)現(xiàn)了這3個(gè)特性。 原子性(atomicity): 由Java內(nèi)存模型來(lái)直接保證原子性變量操作包括...
摘要:如何在線程池中提交線程內(nèi)存模型相關(guān)問(wèn)題什么是的內(nèi)存模型,中各個(gè)線程是怎么彼此看到對(duì)方的變量的請(qǐng)談?wù)動(dòng)惺裁刺攸c(diǎn),為什么它能保證變量對(duì)所有線程的可見性既然能夠保證線程間的變量可見性,是不是就意味著基于變量的運(yùn)算就是并發(fā)安全的請(qǐng)對(duì)比下對(duì)比的異同。 并發(fā)編程高級(jí)面試面試題 showImg(https://upload-images.jianshu.io/upload_images/133416...
摘要:有可能一個(gè)線程中的動(dòng)作相對(duì)于另一個(gè)線程出現(xiàn)亂序。當(dāng)實(shí)際輸出取決于線程交錯(cuò)的結(jié)果時(shí),這種情況被稱為競(jìng)爭(zhēng)條件。這里的問(wèn)題在于代碼塊不是原子性的,而且實(shí)例的變化對(duì)別的線程不可見。這種不能同時(shí)在多個(gè)線程上執(zhí)行的部分被稱為關(guān)鍵部分。 為什么要額外寫一篇文章來(lái)研究volatile呢?是因?yàn)檫@可能是并發(fā)中最令人困惑以及最被誤解的結(jié)構(gòu)。我看過(guò)不少解釋volatile的博客,但是大多數(shù)要么不完整,要么難...
閱讀 1715·2021-11-18 10:02
閱讀 2230·2021-11-15 11:38
閱讀 2680·2019-08-30 15:52
閱讀 2204·2019-08-29 14:04
閱讀 3242·2019-08-29 12:29
閱讀 2095·2019-08-26 11:44
閱讀 1005·2019-08-26 10:28
閱讀 843·2019-08-23 18:37