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