日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

談談對Java中的volatile的理解

瀏覽:170日期:2022-08-21 14:27:25

前言

volatile相關的知識其實自己一直都是有掌握的,能大概講出一些知識,例如:它可以保證可見性;禁止指令重排。這兩個特性張口就來,但要再往深了問,具體是如何實現這兩個特性的,以及在什么場景下使用volatile,為什么不直接用synchronized這種深入和擴展相關的問題,就回答的不好了。因為volatile是面試必問的知識,所以這次準備把這部分知識也給啃掉。

系統處理效率與Java內存模型

在計算機中,每條程序指令都是在CPU中執行的,而CPU執行指令的數據都是臨時存儲在內存中的,但是CPU的執行速度遠超內存的讀取速度,如果所有的CPU指令都是通過內存來讀取數據的話那么將大大的降低了系統的處理效率,所以現代計算機系統都不得不加入一層或多層讀寫速度盡可能接近處理器運算速度的高速緩存(Cache)來作為內存與處理器之間的緩沖。

將運算需要使用的數據復制到緩存中,讓運算能快速進行,當運算結束后,在從緩存同步回內存之中,這樣處理器就無須等待緩慢的內存讀寫了。

雖然說增加了高速緩存提高了CPU的處理效率,但是也帶來了新的問題 :

現代計算機都是多核CPU,一開始,內存中的變量A的值是1,第一個CPU讀取了數據,第二個CPU也將數據讀取到了自己的高速緩存當中,當第一個CPU對變量A進行加1操作時,變量A的值變成了2,然后將將變量A的值寫回內存中,這時第二個CPU也對變量A進行加1操作時,由于第二個CPU中高速緩存中的值還是1,所以加1操作后的結果為2,然后第二個CPU又將變量A的值同步回內存中,這樣就導致執行了兩次加1操作后,變量A的值最終是2,而不是3。這種被多個CPU訪問的變量,通常稱為共享變量。而產生的上面的問題,就是引入了高速緩存后的,主內存和緩存內容不一致的問題。因為每個處理器有自己的高速緩存,但是它們又共享同一塊主內存,所以必然會出現主內存不知該以哪個高速緩存中的變量為準的情況。

談談對Java中的volatile的理解

上面這個緩存不一致的問題,我們先記下來,繼續來看Java內存模型,其實Java內存模型描述的上面講的計算機系統高速緩存和內存之間的關系類似。

Java內存模型描述了,各種變量的訪問規則,以及將變量存儲到內存和從內存讀取變量的這種底層細節。

在Java內存模型中關注的變量都是共享變量(實例變量、類變量)。所有的共享變量都是存儲在主內存中的,但是每個線程在訪問變量的時候也都會在自己的工作內存(處理器高速緩存)中保留一份共享變量的副本。

Java內存模型(Java Memory Model,簡稱JMM)規定:

線程對變量的所有操作(讀,寫)都必須在工作內存中進行,不能直接操作主內存中的數據。不同線程之間 也不能直接訪問對方工作內存中的變量,線程間的變量值傳遞必須通過主內存進行中轉傳遞。在JMM中工作內存和主內存的關系如下圖:

談談對Java中的volatile的理解

Volatile的可見性(保證立即可見)

繼續我們上面的緩存一致性的問題,這個問題,在Java內存模型中,就是可見性的問題,即一個線程修改了共享變量的值,對另一個線程來說是不是立即可見的。如果不是立即可見的,那么就會出現緩存一致性的問題,如果是立即可見的,那么另一個線程在進行操作的時候,拿到的變量值就是最新的。就可以解決可見性的問題。

那么怎么解決可見性問題呢?

方案一:加鎖

將共享變量加鎖,無論是synchronized還是Lock都可以,加鎖達到的目的是在同一時間內只能有一個線程能對共享變量進行操作,就是說,共享變量從讀取到工作內存到更新值后,同步回主內存的過程中,其他線程是操作不了這個變量的。這樣自然就解決了可見性的問題了,但是這樣的效率比較低,操作不了共享變量的線程就只能阻塞。

方案二:volatile修飾修飾共享變量

當一個共享變量被volatile修飾后,會保證每個線程將變量修改后的值立即同步回主內存中,當其他線程有需要讀取變量時會讀取到最新的變量值。

那么volatile做了些什么操作就能解決可見性的問題呢?

被volatile修飾的變量,在被線程操作時,會有這樣的機制:

就是線程對變量操作時會從主內存中讀取到自己的工作內存中,當線程對變量進行了修改后,那么其他已經讀取了此變量的線程中的變量副本就會失效,這樣其他線程在使用變量的時候,發現已經失效,那么就會去主內存中重新獲取,這樣獲取到的就只最新的值了。

那么volatile這個關鍵字是如何實現這套機制的呢?

因為一臺計算機有多臺CPU,同一個變量,在多個CPU中緩存的值有可能不一樣,那么以誰緩存的值為準呢?

既然大家都有自己的值,那么各個CPU間就產生了一種協議,來保證按照一定的規律為準,來確定共享變量的準確值,這樣各個CPU在讀寫共享變量時都按照協議來操作。

這就是緩存一致性協議。

最著名的緩存一致性協議就是Intel的MESI了,說MESI時,先解釋一下,緩存行:

緩存行(cache line):CPU高速緩存的中可以分配的最小存儲單位,高速緩存中的變量都是存在緩存行中的。

MESI的核心思想就是,當CPU對變量進行寫操作時發現,變量是共享變量,那么就會通知其他CPU中將該變量的緩存行設置為無效狀態。當其他CPU在操作變量時發現此變量在的緩存行已經無效,那么就會去主內存中重新讀取最新的變量。

那么其他CPU是如何發現變量被修改了的呢?

因為CPU和其他部件的進行通信是通過總線來進行的,所以每個CPU通過嗅探總線上的傳播數據,來檢查自己緩存的值是不是過期了,當處理器發現自己換成行對應的內存地址被修改后,就會將自己工作內存中的緩存行設置成無須狀態,當CPU對此變量進行修改時會重新從系統主內存中讀取變量。

談談對Java中的volatile的理解

Volatile的有序性(禁止指令重排)

一般來說,我們寫程序的時候,都是要把先代碼從上往下寫,默認的認為程序是自頂向下順序執行的,但是CPU為了提高效率,在保證最終結果準確的情況下,是會對指令進行重新排序的。就是說寫在前的代碼不一定先執行,在后面的也不一定晚執行。

舉個例子:

int a = 5; // 代碼1int b = 8; // 代碼2a = a + 4;// 代碼3int c = a + b;// 代碼4

上面四行代碼的執行順序有可能是

談談對Java中的volatile的理解

JMM在是允許指令重排序的,在保證最后結果正確的情況下,處理器可以盡情的發揮,提高執行效率。

當多個線程執行代碼的時候重排序的情況就更為突出了,各個CPU為了提高自己的效率,有可能會產生競爭情況,這樣就有可能導致最終執行的正確性。

所以為了保證在多個線程下最終執行的正確性,將變量用volatile進行修飾,這樣就會達到禁止指令重排序的效果(其實也可以通過加鎖,還有一些其他已知規則來實現禁止指令重排序,但是我們這里只討論volatile的實現方式)。

那么volatile是如何實現指令重排序的呢?

答案是:內存屏障

內存屏障是一組CPU指令,用于實現對內存操作的順序限制。Java編譯器,會在生成指令系列時,在適當的位置會插入內存屏障來禁止處理器對指令的重新排序。

volatile會在變量寫操作的前后加入兩個內存屏障,來保證前面的寫指令和后面的讀指令是有序的。

談談對Java中的volatile的理解

volatile在變量的讀操作后面插入兩個指令,禁止后面的讀指令和寫指令重排序。

談談對Java中的volatile的理解

有序性,不僅只有volatile能保證,其他的實現方式也能保證,但是如果每一種實現方式都要了解那對于開發人員來說就比較困難了。

所以從JDK5就出現了happen-before原則,也叫先行發生原則。先行發生原則總結起來就是:如果一個操作A的產生的影響能被另一個操作B觀察到,那么可以說,這個操作A先行發生與操作B。

這里所說的影響包括內存中的變量的修改,調用了方法,發送量消息等。

volatile中的先行發生原則是,對一個volatile變量的寫操作,先行發生于后面任何地方對這個變量的讀操作。

Volatile無法保證原子性

原子性,是指一個操作過程要么都成功,要么都失敗,是一個獨立的完整的。

就像上面說的,如果多個線程對一個變量進行累加,那么肯定得不到想要的結果,因為累加就不是一個原子操作。

要保證累加最終結果正確,要么對累加變量加鎖,要么就用AotomicInteger這樣的變量。

/** * 雙重檢查加鎖式單例 */public class DoubleCheckLockSingleton implements Serializable{ /** * 靜態變量,用來存放實例。 */ private volatile static DoubleCheckLockSingleton doubleCheckLockSingleton = null; /** * 私有化構造方法,禁止外部創建實例。 */ private DoubleCheckLockSingleton(){} /** * 雙重檢查加鎖的方式保證線程安全又能獲得到唯一實例 * @return */ public static DoubleCheckLockSingleton getInstance(){ //第一次檢查實例是否已經存在,不存在則進入代碼塊 if(null == doubleCheckLockSingleton){ synchronized (DoubleCheckLockSingleton.class){//第二次檢查if(null==doubleCheckLockSingleton){ doubleCheckLockSingleton = new DoubleCheckLockSingleton();} } } return doubleCheckLockSingleton; }}

為什么要進行雙重檢查呢?

當第一個線程走到第一次檢查時發現對象為空,然后進入鎖,第二次就檢查時也為空,那么就去創建對象,但是這個時候又來了一個線程來到了第一次檢查,發現為空,但是這個時候因為鎖被占用,所以就只能阻塞等待,然后第一個線程創建對象成功了,由于對象是被volatile修飾的能夠立即反饋到其他線程上,所以在第一個線程釋放鎖之后,第二個線程進入了鎖,然后進行第二次檢查時,發現對象已經被創建了,那么就不在創建對象了。從而保證的單例。

還有就是如果創建對象,步驟:

分配內存空間。 調用構造器,實例化。 返回內存地址給引用。

如果這三個指令順序被重排了,那么當多線程來獲取對象的時候就會造成對象雖然實例化了,但是沒有分配內存空間,會有空指針的風險。所以加上了volatile的對象,也保證了在第二次檢查時不會被已經在創建過程中的對象有被檢測為空的風險。

總結一下

volatile其實可以看作是輕量級的synchronized,雖然說volatile不能保證原子性,但是如果在多線程下的操作本身就是原子性操作(例如賦值操作),那么使用volatile會由于synchronized。

volatile可以適用于,某個標識flag,一旦被修改了就需要被其他線程立即可見的情況。也可以修飾作為觸發器的變量,一旦變量被任何一個線程修改了,就去觸發執行某個操作。

volatile的變量寫操作happen-before,后面任何對此volatile變量的讀操作。

到此這篇關于談談對Java中的volatile的理解的文章就介紹到這了,更多相關Java中的volatile內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲激情欧美| 在线亚洲人成| 香蕉成人av| 亚洲午夜天堂| 久久精品91| 久久中文亚洲字幕| 亚洲激情黄色| 亚洲一二av| 久久激情五月婷婷| 麻豆一区二区三区| 大香伊人久久精品一区二区| 成人在线视频区| 日韩中文在线播放| 自由日本语亚洲人高潮| 一区二区三区午夜视频| 日韩成人三级| av一区二区高清| 欧洲激情综合| 免费久久99精品国产| 日日夜夜免费精品视频| 欧美视频久久| 国产一区二区三区亚洲综合| 久久在线免费| 久久国产精品久久久久久电车| 亚洲精品三级| 国产亚洲一卡2卡3卡4卡新区| 九九九精品视频| 在线中文字幕播放| 黄色成人精品网站| 日本a口亚洲| 精品精品99| 国产麻豆一区二区三区精品视频| 97久久超碰| 成人av三级| 视频一区国产视频| 国产精品扒开腿做爽爽爽软件| 日韩在线二区| 首页欧美精品中文字幕| 国产欧美一区| 久久精品av| 日韩精品a在线观看91| 青青青免费在线视频| 水野朝阳av一区二区三区| 国产精东传媒成人av电影| 精品亚洲美女网站| 日本精品在线播放| 国产精品精品| 久久亚洲二区| 欧美1区2区3| 国产字幕视频一区二区| 国产午夜久久av| 成人看片网站| 日韩avvvv在线播放| 亚洲成人国产| 欧美日韩一区二区三区不卡视频| 欧美精选视频一区二区| 日本久久一区| 九九综合在线| 麻豆国产精品视频| 丝袜美腿一区二区三区| 成人污污视频| 日韩在线麻豆| 亚洲黄色中文字幕| 奇米色欧美一区二区三区| 久久久噜噜噜| 国产精品欧美大片| 欧美午夜不卡| 精品五月天堂| 免费看黄色91| 日本精品影院| 国产精品任我爽爆在线播放 | 国产精品免费99久久久| 欧美日韩国产高清电影| 国产第一亚洲| 国产亚洲精品美女久久久久久久久久| 色一区二区三区| 国产欧美丝祙| 中文字幕视频精品一区二区三区| 涩涩av在线| 麻豆精品在线视频| 日韩精品视频中文字幕| 精品中文一区| 超碰99在线| 国产精品观看| 日本午夜精品久久久久| 男女男精品网站| 激情综合网五月| 三上悠亚国产精品一区二区三区| 久久99免费视频| 欧美久久精品| 国产农村妇女精品一二区 | 久久只有精品| 欧美亚洲tv| 最新国产精品| 久久国产福利| 最新日韩av| 久久久精品久久久久久96 | 欧美日韩在线二区| 亚洲欧洲高清| 91视频一区| 国产伊人久久| 精品一区二区三区亚洲| 蜜桃91丨九色丨蝌蚪91桃色| 国产99久久| 九九精品调教| 成人va天堂| 日本在线高清| 欧美成a人国产精品高清乱码在线观看片在线观看久 | av免费不卡国产观看| 久久一区视频| 精品一区二区三区视频在线播放| 国产欧美日本| 国产精品毛片久久久| 国产精品久久久久9999高清| 国产乱子精品一区二区在线观看 | 亚洲欧美网站| 国产精品婷婷| 老牛影视一区二区三区| 蜜臀久久久99精品久久久久久| 噜噜噜躁狠狠躁狠狠精品视频| 亚洲制服少妇| 亚洲欧美日本国产| 日本成人在线一区| 欧美日韩视频免费看| 91欧美极品| 国产欧美日韩在线一区二区| 国产精品九九| 欧美黄页在线免费观看| 美女视频网站久久| 红杏一区二区三区| 免费一二一二在线视频| 在线观看精品| 欧美午夜不卡| 亚洲精品无吗| 国产情侣久久| 国产精品久久久久久久久久10秀| 欧美少妇精品| 午夜久久美女| 日韩激情中文字幕| 国产精品hd| 日韩毛片视频| 亚洲激情av| 免费欧美在线视频| 69堂免费精品视频在线播放| 欧美日本二区| 国产精品久久观看| 精品91久久久久| 日本免费新一区视频| 欧美国产三级| 日本精品在线中文字幕| 99国产精品视频免费观看一公开| 亚洲涩涩av| 久久超级碰碰| 91精品国产福利在线观看麻豆| 午夜亚洲一区| 国产精东传媒成人av电影| 国产资源在线观看入口av| 免费一二一二在线视频| 老司机精品久久| 国产精品亲子伦av一区二区三区| 欧美国产小视频| 夜夜精品视频| 国产福利亚洲| 午夜久久福利| 国产黄色一区| 亚洲成人精选| 国产精品红桃| 日韩一区二区久久| 美女在线视频一区| 狠狠干成人综合网| 欧美精品aa| 99视频精品| 国产精品一在线观看| 日韩啪啪电影网| 香蕉久久一区| 亚洲欧洲高清| 亚洲久久在线| 丝袜诱惑一区二区| 日韩精品视频中文字幕| 日韩欧美不卡| 91成人精品观看| 国产91久久精品一区二区| 91精品国产经典在线观看| 三上悠亚国产精品一区二区三区 | 国产精品啊v在线| 久久久久久久久久久妇女| 快she精品国产999| 福利在线免费视频| 日韩精品久久久久久久软件91| 日韩中文在线电影| 日韩av影院| 不卡一区2区| 精品视频久久| 四虎在线精品| 欧美日韩国产在线观看网站| 国产精品一国产精品k频道56| 免费精品国产| 精品久久99| 久久国产成人| 九九精品调教|