【并發(fā)編程】- 內(nèi)存模型(針對JSR-133內(nèi)存模型)篇

并發(fā)編程模型

  • 1.兩個關鍵問題

  • 1)線程之間如何通信

  • 共享內(nèi)存
    程之間共享程序的公共狀態(tài)宵距,通過寫-讀內(nèi)存中的公共狀態(tài)進行隱式通信

  • 消息傳遞
    程之間沒有公共狀態(tài)蒜胖,線程之間必須通過發(fā)送消息來顯式進行通信

  • 2)線程之間如何同步

  • 線程之間沒有公共狀態(tài),線程之間必須通過發(fā)送消息來顯式進行通信

總結(jié):Java的并發(fā)采用的是共享內(nèi)存模型歧蒋,Java線程之間的通信總是隱式進行醉锄,整個通信過程對程序員完全透明。

  • 2.抽象結(jié)構(gòu)

  • 1)本地內(nèi)存

  • 每個線程都有一個私有的本地內(nèi)存(LocalMemory)龙亲,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本陕凹。本地內(nèi)存是JMM的一個抽象概念,并不真實存在鳄炉。它涵蓋了緩存杜耙、寫緩沖區(qū)、寄存器以及其他的硬件和編譯器優(yōu)化

  • 2)主內(nèi)存

  • 線程之間的共享變量存儲在主內(nèi)存

附圖:


image

注:所有實例域拂盯、靜態(tài)域和數(shù)組元素都存儲在堆內(nèi)存中佑女,堆內(nèi)存在線程之間共享(本章用“共享變量”這個術(shù)語代指實例域,靜態(tài)域和數(shù)組元素)。局部變量(Local Variables)团驱,方法定義參數(shù)(Java語法規(guī)范稱之為Formal Method Parameters)和異常處理器參數(shù)(Exception HandlerParameters)不會在線程之間共享摸吠,它們不會有內(nèi)存可?性問題,也不受內(nèi)存模型的影響嚎花。


  • 3.重排序


  • 定義:重排序是指編譯器和處理器為了優(yōu)化程序性能而對指令序列進行重新排序的一種手段寸痢。

  • 1) 3種類型

  • 編譯器優(yōu)化的重排序(處理器重排序)
  • 編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執(zhí)行順序
  • 指令級并行的重排序(處理器重排序)
image

現(xiàn)代處理器采用了指令級并行技術(shù)(Instruction-Level Parallelism紊选,ILP)來將多條指令重疊執(zhí)行啼止。如果不存在數(shù)據(jù)依賴性,處理器可以改變語句對應機器指令的執(zhí)行順序
內(nèi)存系統(tǒng)的重排序
由于處理器使用緩存和讀/寫緩沖區(qū)兵罢,這使得加載和存儲操作看上去可能是在亂序執(zhí)行

  • 2)數(shù)據(jù)依賴性


  • 說明:如果兩個操作訪問同一個變量献烦,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數(shù)據(jù)依賴性卖词。

  • 存在三種類型(只要重排兩個操作執(zhí)行順序巩那,結(jié)果便會被改變)

    • 寫后讀:寫一個變量之后,再讀這個變量
    • 寫后寫:寫一個變量之后此蜈,在寫這個變量
    • 讀后寫:讀一個變量之后拢操,再寫這個變量
  • 3)as-if-serial語義

  • 說明:不管怎么重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變舶替。編譯器令境、runtime和處理器都必須遵守as-if-serial語義。
    • 順序規(guī)則:A?happens-before B,B?happens-before C,happens-before C
      那么實際執(zhí)行是B可以排在A前顾瞪,JMM允許這種排序舔庶。
  • 4)對多線程的影響

  • 對于存證控制依賴的操作重排序,可能會改變程序的執(zhí)行結(jié)果陈醒。
  • 5)DCL問題(double check lock)

    public class DoubleCheckedLocking { // 1
        private static Instance instance; // 2

        public static Instance getInstance() { // 3
            if (instance == null) { // 4:第一次檢查
                synchronized (DoubleCheckedLocking.class) { // 5:加鎖
                    if (instance == null) // 6:第二次檢查
                        instance = new Instance(); // 7:問題的根源出在這里
                } // 8
            } // 9
            return instance; // 10
        } // 11
    }
  • 那么問題就出現(xiàn)在線程執(zhí)行到第4行惕橙,代碼讀取到instance不為null時,instance引用的對象有可能還
    沒有完成初始化
    钉跷。
    • __根源:
    memory = allocate();? //1:分配對象的內(nèi)存空間
    ctorInstance(memory);  //2:初始化對象
    instance = memory;?? //3:設置instance指向剛分配的內(nèi)存地址
  • JMM允許上述命令的執(zhí)行順序調(diào)整為
    memory = allocate();? //1:分配對象的內(nèi)存空間
    instance = memory;?? //3:設置instance指向剛分配的內(nèi)存地址
                          //注意弥鹦,此時對象還沒有被初始化!
    ctorInstance(memory); //2:初始化對象
  • 問題:為什么要調(diào)整這個順序呢爷辙?
  • 原因:這個重排序在沒有改變單線程程序執(zhí)行結(jié)果的前提下彬坏,可以提高程序的執(zhí)行性能。


    image

    image
  • 解決方案

    • 不允許2和3重排序([volatile]);
    • 允許2和3重排序膝晾,但不允許其他線程“看到”這個重排序栓始。
  • 第一種基于volatile方案

    public class SafeDoubleCheckedLocking {
        private volatile static Instance instance;

        public static Instance getInstance() {
            if (instance == null) {
                synchronized (SafeDoubleCheckedLocking.class) {
                    if (instance == null)
                        instance = new Instance(); // instance為volatile,現(xiàn)在沒問題了
                }
            }
            return instance;
        }
    }
  • 第二種基于類初始化方案
    public class InstanceFactory {
        private static class InstanceHolder {
            public static Instance instance = new Instance();
        }

        public static Instance getInstance() {
            return InstanceHolder.instance;??// 這里將導致InstanceHolder類被初始化血当,存在初始化鎖幻赚,拿不到的線程會一直等待
        }
    }

  • 4.happens-before

happens-before是JMM最核心的概念

1) 關系的定義

  • 如果?個操作happens-before另?個操作禀忆,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見,而且第一個操作的執(zhí)行順序排在第二個操作之前落恼。[JMM對程序員的承諾]
  • 兩個操作之間存在happens-before關系箩退,并不意味著Java平臺的具體實現(xiàn)必須要按照happens-before關系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果佳谦,與按happens-before關系來執(zhí)行的結(jié)果一致戴涝,那么這種重排序并不非法(也就是說,JMM允許這種重排序)[JMM對編譯器和處理器重排序的約束原則]

2) 規(guī)則

  • 程序順序規(guī)則:一個線程中的每個操作吠昭,happens-before于該線程中的任意后續(xù)操作。
  • 監(jiān)視器鎖規(guī)則:對一個鎖的解鎖胧瓜,happens-before于隨后對這個鎖的加鎖矢棚。
  • volatile變量規(guī)則:對一個volatile域的寫,happens-before于任意后續(xù)對這個volatile域的讀府喳。
  • 傳遞性:A happens-before B蒲肋,且B happens-before C,那么A happens-before C钝满。
  • start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動線程B)兜粘,那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
  • join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回弯蚜,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回孔轴。

注意:兩個操作之間具有happens-before關系,并不意味著前
一個操作必須要在后一個操作之前執(zhí)行碎捺!happens-before僅僅要求前一個操
作(執(zhí)行的結(jié)果)對后一個操作可見路鹰,且前一個操作按順序排在第一個操
作之前(the first is visible to and ordered before the second)。

  • appens-before與JMM的關系
    image

話外語:

  • as-if-serial語義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變收厨,happens-before關系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變
  • as-if-serial語義和happens-before這么做的目的晋柱,都是為了在不改變程序執(zhí)行結(jié)果的前提下,盡可能地提高程序執(zhí)行的并行度

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诵叁,一起剝皮案震驚了整個濱河市雁竞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拧额,老刑警劉巖碑诉,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侥锦,居然都是意外死亡联贩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門捎拯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泪幌,“玉大人盲厌,你說我怎么就攤上這事』隼幔” “怎么了吗浩?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長没隘。 經(jīng)常有香客問我懂扼,道長,這世上最難降的妖魔是什么右蒲? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任阀湿,我火速辦了婚禮,結(jié)果婚禮上瑰妄,老公的妹妹穿的比我還像新娘陷嘴。我一直安慰自己,他們只是感情好间坐,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布灾挨。 她就那樣靜靜地躺著,像睡著了一般竹宋。 火紅的嫁衣襯著肌膚如雪劳澄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天蜈七,我揣著相機與錄音秒拔,去河邊找鬼。 笑死飒硅,一個胖子當著我的面吹牛溯警,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狡相,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼梯轻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尽棕?” 一聲冷哼從身側(cè)響起喳挑,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滔悉,沒想到半個月后伊诵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡回官,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年曹宴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歉提。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡笛坦,死狀恐怖区转,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情版扩,我是刑警寧澤废离,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站礁芦,受9級特大地震影響蜻韭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柿扣,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一肖方、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧未状,春花似錦俯画、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烹骨。三九已至翻伺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沮焕,已是汗流浹背吨岭。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峦树,地道東北人辣辫。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像魁巩,于是被迫代替她去往敵國和親急灭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容