最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 內(nèi)存模型中的happen-before關(guān)系詳解

 更新時間:2022年10月31日 11:05:42   作者:????????????  
這篇文章主要為大家介紹了Java 內(nèi)存模型中的happen-before關(guān)系示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

Java 語言在設(shè)計之初就引入了線程的概念,以充分利用現(xiàn)代處理器的計算能力,這既帶來了強大、靈活的多線程機(jī)制,也帶來了線程安全等令人混淆的問題,而 Java 內(nèi)存模型(Java Memory Model,JMM)為我們提供了一個在紛亂之中達(dá)成一致的指導(dǎo)準(zhǔn)則。

本篇博文的重點是,Java 內(nèi)存模型中的 happen-before 是什么?

概述

Happen-before 關(guān)系,是 Java 內(nèi)存模型中保證多線程操作可見性的機(jī)制,也是對早期語言規(guī)范中含糊的可見性概念的一個精確定義。

它的具體表現(xiàn)形式,包括但遠(yuǎn)不止是我們直覺中的 synchronized、volatile、lock 操作順序等方面,例如:

  • 線程內(nèi)執(zhí)行的每個操作,都保證 happen-before 后面的操作,這就保證了基本的程序順序規(guī)則,這是開發(fā)者在書寫程序時的基本約定。
  • 對于 volatile 變量,對它的寫操作,保證 happen-before 在隨后對該變量的讀取操作。
  • 對于一個鎖的解鎖操作,保證 happen-before 加鎖操作。
  • 對象構(gòu)建完成,保證 happen-before 于 finalizer 的開始動作。
  • 甚至是類似線程內(nèi)部操作的完成,保證 happen-before 其他 Thread.join() 的線程等。

這些 happen-before 關(guān)系是存在著傳遞性的,如果滿足 a happen-before b 和 b happen-before c,那么 a happen-before c 也成立。

前面我一直用 happen-before,而不是簡單說前后,是因為它不僅僅是對執(zhí)行時間的保證,也包括對內(nèi)存讀、寫操作順序的保證。僅僅是時鐘順序上的先后,并不能保證線程交互的可見性。

為什么需要 JMM,它試圖解決什么問題?

Java 是最早嘗試提供內(nèi)存模型的語言,這是簡化多線程編程、保證程序可移植性的一個飛躍。早期類似 C、C++ 等語言,并不存在內(nèi)存模型的概念(C++ 11 中也引入了標(biāo)準(zhǔn)內(nèi)存模型),其行為依賴于處理器本身的內(nèi)存一致性模型,但不同的處理器可能差異很大,所以一段 C++ 程序在處理器 A 上運行正常,并不能保證其在處理器 B 上也是一致的。

即使如此,最初的 Java 語言規(guī)范仍然是存在著缺陷的,當(dāng)時的目標(biāo)是,希望 Java 程序可以充分利用現(xiàn)代硬件的計算能力,同時保持“書寫一次,到處執(zhí)行”的能力。

但是,顯然問題的復(fù)雜度被低估了,隨著 Java 被運行在越來越多的平臺上,人們發(fā)現(xiàn),過于泛泛的內(nèi)存模型定義,存在很多模棱兩可之處,對 synchronized 或 volatile 等,類似指令重排序時的行為,并沒有提供清晰規(guī)范。這里說的指令重排序,既可以是編譯器優(yōu)化行為,也可能是源自于現(xiàn)代處理器的亂序執(zhí)行等。

換句話說:

  • 既不能保證一些多線程程序的正確性,例如最著名的就是雙檢鎖(Double-Checked Locking,DCL)的失效問題,雙檢鎖可能導(dǎo)致未完整初始化的對象被訪問,理論上這叫并發(fā)編程中的安全發(fā)布(Safe Publication)失敗。
  • 也不能保證同一段程序在不同的處理器架構(gòu)上表現(xiàn)一致,例如有的處理器支持緩存一致性,有的不支持,各自都有自己的內(nèi)存排序模型。

所以,Java 迫切需要一個完善的 JMM,能夠讓普通 Java 開發(fā)者和編譯器、JVM 工程師,能夠清晰地達(dá)成共識。換句話說,可以相對簡單并準(zhǔn)確地判斷出,多線程程序什么樣的執(zhí)行序列是符合規(guī)范的。

所以:

  • 對于編譯器、JVM 開發(fā)者,關(guān)注點可能是如何使用類似內(nèi)存屏障(Memory-Barrier)之類技術(shù),保證執(zhí)行結(jié)果符合 JMM 的推斷。
  • 對于 Java 應(yīng)用開發(fā)者,則可能更加關(guān)注 volatile、synchronized 等語義,如何利用類似 happen-before 的規(guī)則,寫出可靠的多線程應(yīng)用,而不是利用一些“秘籍”去糊弄編譯器、JVM。

我畫了一個簡單的角色層次圖,不同工程師分工合作,其實所處的層面是有區(qū)別的。JMM 為 Java 工程師隔離了不同處理器內(nèi)存排序的區(qū)別,這也是為什么我通常不建議過早深入處理器體系結(jié)構(gòu),某種意義上來說,這樣本就違背了 JMM 的初衷。

JMM 是怎么解決可見性等問題的呢?

在這里有必要簡要介紹一下典型的問題場景。

【JAVA】JVM 內(nèi)存區(qū)域的劃分 里介紹了 JVM 內(nèi)部的運行時數(shù)據(jù)區(qū),但是真正程序執(zhí)行,實際是要跑在具體的處理器內(nèi)核上。你可以簡單理解為,把本地變量等數(shù)據(jù)從內(nèi)存加載到緩存、寄存器,然后運算結(jié)束寫回主內(nèi)存。你可以從下面示意圖,看這兩種模型的對應(yīng)。

看上去很美好,但是當(dāng)多線程共享變量時,情況就復(fù)雜了。試想,如果處理器對某個共享變量進(jìn)行了修改,可能只是體現(xiàn)在該內(nèi)核的緩存里,這是個本地狀態(tài),而運行在其他內(nèi)核上的線程,可能還是加載的舊狀態(tài),這很可能導(dǎo)致一致性的問題。從理論上來說,多線程共享引入了復(fù)雜的數(shù)據(jù)依賴性,不管編譯器、處理器怎么做重排序,都必須尊重數(shù)據(jù)依賴性的要求,否則就打破了正確性!這就是 JMM 所要解決的問題。

JMM 內(nèi)部的實現(xiàn)通常是依賴于所謂的內(nèi)存屏障,通過禁止某些重排序的方式,提供內(nèi)存可見性保證,也就是實現(xiàn)了各種 happen-before 規(guī)則。與此同時,更多復(fù)雜度在于,需要盡量確保各種編譯器、各種體系結(jié)構(gòu)的處理器,都能夠提供一致的行為。

我以 volatile 為例,看看如何利用內(nèi)存屏障實現(xiàn) JMM 定義的可見性?

對于一個 volatile 變量:

  • 對該變量的寫操作之后,編譯器會插入一個寫屏障。
  • 對該變量的讀操作之前,編譯器會插入一個讀屏障

內(nèi)存屏障能夠在類似變量讀、寫操作之后,保證其他線程對 volatile 變量的修改對當(dāng)前線程可見,或者本地修改對其他線程提供可見性。換句話說,線程寫入,寫屏障會通過類似強迫刷出處理器緩存的方式,讓其他線程能夠拿到最新數(shù)值。

如果你對更多內(nèi)存屏障的細(xì)節(jié)感興趣,或者想了解不同體系結(jié)構(gòu)的處理器模型,建議參考 JSR-133 相關(guān)文檔,我個人認(rèn)為這些都是和特定硬件相關(guān)的,內(nèi)存屏障之類只是實現(xiàn) JMM 規(guī)范的技術(shù)手段,并不是規(guī)范的要求。

從應(yīng)用開發(fā)者的角度,JMM 提供的可見性,體現(xiàn)在類似 volatile 上,具體行為是什么樣呢?

我這里循序漸進(jìn)的舉兩個例子。

首先,請看下面的代碼片段,希望達(dá)到的效果是,當(dāng) condition 被賦值為 false 時,線程 A 能夠從循環(huán)中退出。

// Thread A
while (condition) {
}
// Thread B
condition = false;

這里就需要 condition 被定義為 volatile 變量,不然其數(shù)值變化,往往并不能被線程 A 感知,進(jìn)而無法退出。當(dāng)然,也可以在 while 中,添加能夠直接或間接起到類似效果的代碼。

第二,我想舉 Brian Goetz 提供的一個經(jīng)典用例,使用 volatile 作為守衛(wèi)對象,實現(xiàn)某種程度上輕量級的同步,請看代碼片段:

Map configOptions;
char[] configText;
volatile boolean initialized = false;
// Thread A
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
// Thread B
while (!initialized)
  sleep();
// use configOptions

JSR-133 重新定義的 JMM 模型,能夠保證線程 B 獲取的 configOptions 是更新后的數(shù)值。

也就是說 volatile 變量的可見性發(fā)生了增強,能夠起到守護(hù)其上下文的作用。線程 A 對 volatile 變量的賦值,會強制將該變量自己和當(dāng)時其他變量的狀態(tài)都刷出緩存,為線程 B 提供可見性。當(dāng)然,這也是以一定的性能開銷作為代價的,但畢竟帶來了更加簡單的多線程行為。

我們經(jīng)常會說 volatile 比 synchronized 之類更加輕量,但輕量也僅僅是相對的,volatile 的讀、寫仍然要比普通的讀寫要開銷更大,所以如果你是在性能高度敏感的場景,除非你確定需要它的語義,不然慎用。

后記

以上就是 【JAVA】Java 內(nèi)存模型中的 happen-before 的所有內(nèi)容了;

從 happen-before 關(guān)系開始,幫你理解了什么是 Java 內(nèi)存模型。為了更方便理解,我作了簡化,從不同工程師的角色劃分等角度,闡述了問題的由來,以及 JMM 是如何通過類似內(nèi)存屏障等技術(shù)實現(xiàn)的。最后,我以 volatile 為例,分析了可見性在多線程場景中的典型用例。

更多關(guān)于Java happen before的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java基于OpenGL ES實現(xiàn)渲染實例

    java基于OpenGL ES實現(xiàn)渲染實例

    這篇文章主要介紹了java基于OpenGL ES實現(xiàn)渲染,實例分析了OpenGL渲染操作的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • SpringBoot整合Mybatis實現(xiàn)CRUD

    SpringBoot整合Mybatis實現(xiàn)CRUD

    這篇文章主要介紹了SpringBoot整合Mybatis實現(xiàn)CRUD的相關(guān)知識,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • Java正則表達(dá)式matcher.group()用法代碼

    Java正則表達(dá)式matcher.group()用法代碼

    這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式matcher.group()用法的相關(guān)資料,最近在做一個項目,需要使用matcher.group()方法匹配出需要的內(nèi)容,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2023-08-08
  • Java正則表達(dá)式易錯知識點匯總

    Java正則表達(dá)式易錯知識點匯總

    這篇文章主要總結(jié)Java正則表達(dá)式易錯知識,對易錯知識點進(jìn)行分類整理,幫助大家更好的學(xué)習(xí)Java正則表達(dá)式,感興趣的小伙伴們可以參考一下
    2015-12-12
  • Java的LinkedHashMap的實現(xiàn)原理詳解

    Java的LinkedHashMap的實現(xiàn)原理詳解

    這篇文章主要介紹了Java的LinkedHashMap的實現(xiàn)原理詳解,???LinkedHashMap是Map接口的哈希表和鏈接列表實現(xiàn),具有可預(yù)知的迭代順序,此實現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵,此類不保證映射的順序,特別是它不保證該順序恒久不變,需要的朋友可以參考下
    2023-09-09
  • springboot自定義Starter過程解析

    springboot自定義Starter過程解析

    這篇文章主要介紹了springboot自定義Starter過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 圖解Java排序算法之希爾排序

    圖解Java排序算法之希爾排序

    這篇文章主要為大家詳細(xì)介紹了Java排序算法之希爾排序,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java實現(xiàn)二叉堆、大頂堆和小頂堆

    Java實現(xiàn)二叉堆、大頂堆和小頂堆

    二叉堆就是完全二叉樹,或者是靠近完全二叉樹結(jié)構(gòu)的二叉樹。大頂堆要求對于一個節(jié)點來說,它的左右節(jié)點都比它??;小頂堆要求對于一個節(jié)點來說,它的左右節(jié)點都比它大。本文將用Java分別實現(xiàn)二叉堆、大頂堆和小頂堆。需要的可以參考一下
    2022-01-01
  • Spring和activiti進(jìn)行整合過程解析

    Spring和activiti進(jìn)行整合過程解析

    這篇文章主要介紹了Spring和activiti進(jìn)行整合過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • 詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié))

    詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié))

    本篇文章主要介紹了詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié)),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-11-11

最新評論

时尚| 瑞安市| 枣强县| 乌兰浩特市| 庆云县| 屯留县| 通海县| 阿拉善右旗| 永德县| 澜沧| 蚌埠市| 咸阳市| 无极县| 成武县| 揭西县| 芦山县| 兴山县| 潮州市| 贵溪市| 当涂县| 蒲城县| 福海县| 收藏| 东乌珠穆沁旗| 普安县| 合山市| 景洪市| 澄迈县| 藁城市| 清涧县| 沽源县| 太谷县| 文山县| 页游| 松滋市| 卫辉市| 宁海县| 鄂州市| 兴义市| 靖州| 民权县|