并發(fā)系列之volatile

加州大學圣地亞哥分校(美國)校訓:“愿知識之光普照大地⌒⒚埃”


夏季柬姚,收獲的季節(jié),可看著A股大盤一直趴著庄涡,又是徒勞了半年量承,不免稍有傷神。罷了罷了穴店,聊聊可愛的技術吧撕捍,這東西才是養(yǎng)家糊口的根本。這年頭泣洞,任何技能必須具有強大的變現(xiàn)能力忧风,如果NO,請換道球凰∈ㄍ龋可能很多朋友們不認同,俗話說:三百六十行行行出狀元呕诉。OK蚤霞,比如送外賣的小哥,送出劉翔的速度义钉,送出吉尼斯紀錄昧绣,也是肉眼可見的天花板吧。哈哈捶闸,上述純扯淡夜畴,正事開干。


這篇文章我不想簡單地告訴你volatile的語義作用删壮,JMM如何保證可見性等贪绘,咱們其實可以系統(tǒng)聊聊,前世今生還是很有必要的央碟。那就從硬件系統(tǒng)架構(gòu)/多核CPU主存可見性/JVM的底層實現(xiàn)講講税灌,來龍去脈了解清楚了,那才叫真的理解,而不是簡單地死記硬背菱涤,這才算是科學地學習苞也。

一 硬件系統(tǒng)架構(gòu)演變

我們知道,運行在計算機上的程序粘秆,指令是由CPU執(zhí)行的如迟,數(shù)據(jù)是存儲在主存中的,CPU從主存中讀取數(shù)據(jù)執(zhí)行指令攻走,再回寫到主存中殷勘。CPU執(zhí)行指令的速度是非常快的昔搂,但讀取寫入主存相對較慢的玲销,有人拖后腿了,怎么辦呢摘符?大家有沒有聽說過CPU高速緩存贤斜,就是來解決拖后腿問題的。
1/CPU高速緩存
CPU高速緩存為單個CPU所有议慰,只有運行在這個CPU上的線程才能訪問。緩存系統(tǒng)是以緩存行為單位存儲的奴曙,一般是64個字節(jié)别凹。按級分為L1 cache / L2 cache / 多核心共享L3 cache。執(zhí)行流程如下:
a/首先CPU使用自己的寄存器洽糟,然后使用速度更快的L1炉菲,其中L1D緩存數(shù)據(jù),L1I緩存指令坤溃;
b/L1緩存和次快的L2做數(shù)據(jù)同步拍霜,L2緩存和L3做數(shù)據(jù)同步;
c/L3為多個CPU共享的薪介,與主內(nèi)存做數(shù)據(jù)同步祠饺;
2/緩存寫入主存
a/直寫(write-through)
直寫是透過本級緩存,直接把數(shù)據(jù)寫到下一級緩存中汁政,或直接寫到主存中道偷,同時更新緩存中的數(shù)據(jù),緩存行永遠和它對應的內(nèi)存內(nèi)容相匹配记劈。
b/回寫(write-back)
緩存并不會立即把寫操作傳遞到下一級勺鸦,而是僅修改本級緩存中的數(shù)據(jù),并且把對應的緩存數(shù)據(jù)標記為臟數(shù)據(jù)目木,臟數(shù)據(jù)會觸發(fā)回寫换途,即把里面的內(nèi)容寫到對應的內(nèi)存或下一級緩存中,回寫后,臟數(shù)據(jù)就變干凈了军拟。
3/CPU緩存一致性方案
a/通過在總線上加LOCK#鎖的方式
這是一種獨占式的方式剃执,在同一時刻只能運行一個CPU,效率較為低下吻谋;
b/通過緩存一致性協(xié)議忠蝗,保證多核CPU對共享數(shù)據(jù)的可見性,主要有:
窺探技術:
所有內(nèi)存?zhèn)鬏敹及l(fā)生在一條共享的總線上漓拾,對所有CPU可見阁最;每個CPU不停地窺探總線上發(fā)生的數(shù)據(jù)交換,并追蹤其他緩存在做什么骇两。緩存是CPU獨享的速种,內(nèi)存是CPU共享的,緩存訪問內(nèi)存是需要仲裁的低千,即在同一個指令周期中配阵,只有一個緩存可以讀寫內(nèi)存。
MESI協(xié)議:是緩存行四種狀態(tài)的縮寫示血,如下
已修改緩存行(Modified)棋傍,該緩存行已經(jīng)被所屬的CPU修改了,其他CPU持有的該緩存行的拷貝也會變成失效狀態(tài)难审;
獨占緩存行(Exclusive)瘫拣,和主存內(nèi)容保持一致的拷貝,其他CPU持有的這份內(nèi)容的拷貝變成失效狀態(tài)告喊;
共享緩存行(Shared)麸拄,和主存內(nèi)容保持一致的拷貝,其他CPU也可持有拷貝黔姜,但只能讀取拢切,不允許寫入;
無效緩存行(Invalid)秆吵,CPU中的緩存行無效了淮椰;
總結(jié)來看,只有某個CPU獨占了這個緩存行纳寂,才能夠?qū)懭胧蛋刺幱贛或E的狀態(tài);當CPU想讀取該緩存行烈疚,該緩存行必須是共享狀態(tài)黔牵。

二 指令重排序

我們知道,CPU在執(zhí)行指令的時候為了提升性能爷肝,會有一定的指令重排猾浦。執(zhí)行結(jié)果與預期結(jié)果一致陆错,則重排一定是基于規(guī)則,volatile能夠提供一定的有序性金赦,禁止一定的指令重排音瓷。這里介紹下不同級別的重排序,如下:
1/編譯器優(yōu)化的重排序
編譯器在不改變單線程程序的語義前提下夹抗,可重新安排語句的執(zhí)行順序绳慎;
2/指令級并行的重排序
如果不存在數(shù)據(jù)依賴性,處理器可以改變指令的執(zhí)行順序漠烧,采用的是指令級并行技術杏愤,將多條指令重疊執(zhí)行;
3/內(nèi)存系統(tǒng)的重排序
由于處理器使用緩存和讀寫緩沖區(qū)已脓,這使得加載和存儲操作看上去是亂序執(zhí)行珊楼。
小結(jié):就Java程序而言,從java代碼到CPU執(zhí)行序列度液,也要經(jīng)過上述的重排序厕宗。分析來看,重排序會造成內(nèi)存可見性問題堕担。要想解決問題已慢,需要了解重排序遵循的準則,才能找到對應的方案霹购。
這里介紹下指令重排中單線程和多線程遵循的準則:
1/as-if-serial
只針對于單線程運行的程序佑惠,不管怎么怎么重排序,都不會改變執(zhí)行結(jié)果厕鹃。就是這么硬氣兢仰,編譯器/運行時和處理器重排序必須遵循乍丈。這里通俗理解下:主要是對指令之間數(shù)據(jù)具有依賴性禁止重排序剂碴。滿足as-if-serial基準,單線程程序看起來像是按順序執(zhí)行的轻专,避免了內(nèi)存可見性問題忆矛。
2/happen-before
happen-before原則是Java內(nèi)存模型對指令重排的約束,如A happen-before B请垛,則A 操作的結(jié)果將對B可見催训,但實際中A 的執(zhí)行順序未必在B之前,只要執(zhí)行結(jié)果與預期一致即可宗收。JMM基于happen-before 原則保證多線程的內(nèi)存可見性漫拭,具體準則如下:
a/程序順序規(guī)則:一個線程中的每個操作,happen-before于該線程中任意后續(xù)的操作混稽;
b/監(jiān)視器鎖規(guī)則:對一個監(jiān)視器的解鎖采驻,happen-before于隨后對這個監(jiān)視器的加鎖审胚;
c/傳遞性:if A happen-before B,B happen-before C礼旅,則A happen-before C膳叨;
d/線程的start方法happen-before于線程的后續(xù)所有操作;
e/線程上的所有操作happen-before于其他線程在該線程上join返回成功后的操作痘系;
f/volatile變量規(guī)則:對一個volatile域的寫菲嘴,happen-before于任意后續(xù)對這個域的讀。

三 volatile原理

1/基本語義
volatile用來修飾變量或?qū)ο筇洌袃蓪诱Z義:
a/保證可見性龄坪,但不保證原子性;
b/提供一定程度的有序性(happen-before),禁止指令重排序奴璃。
2/原理剖析
jvm是通過內(nèi)存屏障來實現(xiàn)volatile的悉默,內(nèi)存屏障有四種類型,如下:
a/LoadLoad:指令形如Load1苟穆;LoadLoad抄课;Load2,其語義是Load1的裝載要先于Load2的裝載雳旅;
b/LoadStore:指令形如Load1跟磨;LoadStore燕锥;Store2甘邀,其語義是Load1的裝載要先于Store2存儲指令刷新到內(nèi)存百框;
c/StoreLoad:指令形如Store1骇钦;StoreLoad旷档;Load2捏检,其語義是Store1存儲指令刷新到內(nèi)存要先于Load2的裝載惠况;
d/StoreStore:指令形如Store1瑟幕;StoreStore迎变;Store2充尉,其語義是Store1存儲指令刷新到內(nèi)存要先于Store2的存儲;
舉例衣形,以兩個操作讀寫為例看下插入的內(nèi)存屏障驼侠,如下:

操作 普通讀 普通寫 volatile讀 volatile寫
普通讀 LoadStore
普通寫 StoreStore
volatile讀 LoadLoad LoadStore LoadLoad LoadStore
volatile寫 StoreLoad StoreStore

demo世界不孤單,請閱:

/**
 * @author 阿倫故事
 * @Description:測試volatile的作用
 * 對比去掉全局變量num的volatile的修飾看下測試結(jié)果
 * */
@Slf4j
public class VolatileTest {
    //聲明volatile全局變量
    private volatile int num = 0;

    public static void main(String[] args) {
        VolatileTest volatileTest = new VolatileTest();
        //創(chuàng)建一個用于volatile寫的線程并啟動
        new Thread(()->{
            log.info("Thread name:"+Thread.currentThread().getName()+"--sleep");
            try {
                Thread.sleep(500);
                volatileTest.num = 5;
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Thread name:"+Thread.currentThread().getName()+"--dead");
        }).start();
        //創(chuàng)建一個用于volatile讀的線程并啟動
        new Thread(()->{
            log.info("Thread name:"+Thread.currentThread().getName()+"--num="+volatileTest.num);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Thread name:"+Thread.currentThread().getName()+"--dead--num="+volatileTest.num);
        }).start();
    }
}

特此聲明:
分享文章有完整的知識架構(gòu)圖谆吴,將從以下幾個方面系統(tǒng)展開:
1 基礎(Linux/Spring boot/并發(fā))
2 性能調(diào)優(yōu)(jvm/tomcat/mysql)
3 高并發(fā)分布式
4 微服務體系
如果您覺得文章不錯倒源,請關注阿倫故事,您的支持是我堅持的莫大動力句狼,在此受小弟一拜笋熬!


每篇福利:

評論區(qū)打出車型.jpg

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腻菇,隨后出現(xiàn)的幾起案子胳螟,更是在濱河造成了極大的恐慌苫拍,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旺隙,死亡現(xiàn)場離奇詭異绒极,居然都是意外死亡,警方通過查閱死者的電腦和手機蔬捷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門垄提,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人周拐,你說我怎么就攤上這事铡俐。” “怎么了妥粟?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵审丘,是天一觀的道長。 經(jīng)常有香客問我勾给,道長滩报,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任播急,我火速辦了婚禮脓钾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桩警。我一直安慰自己可训,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布捶枢。 她就那樣靜靜地躺著握截,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烂叔。 梳的紋絲不亂的頭發(fā)上谨胞,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音长已,去河邊找鬼畜眨。 笑死昼牛,一個胖子當著我的面吹牛术瓮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贰健,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼胞四,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伶椿?” 一聲冷哼從身側(cè)響起辜伟,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤氓侧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后导狡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體约巷,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年旱捧,在試婚紗的時候發(fā)現(xiàn)自己被綠了独郎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡枚赡,死狀恐怖氓癌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贫橙,我是刑警寧澤贪婉,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站卢肃,受9級特大地震影響疲迂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莫湘,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一鬼譬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逊脯,春花似錦优质、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匕争,卻和暖如春避乏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甘桑。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工拍皮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跑杭。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓铆帽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親德谅。 傳聞我的和親對象是個殘疾皇子爹橱,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348