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

并發(fā)編程之Java內(nèi)存模型

 更新時間:2021年11月04日 08:51:52   作者:李子捌  
這篇文章主要介紹了Java并發(fā)編程之內(nèi)存模型,Java內(nèi)存模型中的順序一致性,主要介紹重排序與順序一致性內(nèi)存模型,下面文章將圍繞Java內(nèi)存模型展開內(nèi)容,需要的小伙伴可以參考一下

簡介:

Java線程之間的通信對程序員完全透明,內(nèi)存可見性問題很容易困擾Java程序員,這一系列幾篇文章將揭開Java內(nèi)存模型的神秘面紗。

這一系列的文章大致分4個部分,分別是:

  • Java內(nèi)存模型基礎,主要介紹內(nèi)存模型相關基本概念
  • Java內(nèi)存模型中的順序一致性,主要介紹重排序與順序一致性內(nèi)存模型
  • 同步原語,主要介紹三個同步原語(synchronizedvolatile和final)的內(nèi)存語義及重排序規(guī)則在處理器中的實現(xiàn)
  • Java內(nèi)存模型的設計,主要介紹Java內(nèi)存模型的設計原理,及其與處理器內(nèi)存模型和順序一致性內(nèi)存模型的關系。

一、Java內(nèi)存模型的基礎

1.1 并發(fā)編程模型的兩個關鍵問題

在并發(fā)編程中需要處理兩個關鍵問題:線程之間如何通信及線程之間如何同步(這里的線程是指并發(fā)執(zhí)行的活動實體)。

通信——線程之間以何種機制來交換信息。在命令式編程中,線程之間的通信機制有兩種:共享內(nèi)存和消息傳遞。

  • 共享內(nèi)存:線程之間共享程序的公共狀態(tài),通過讀寫內(nèi)存中的公共轉臺進行隱式通信
  • 消息傳遞:線程之間沒有公共狀態(tài),線程之間必須通過發(fā)送消息來顯式進行通信

同步——程序中用于控制不同線程鍵操作發(fā)生相對順序的機制。

  • 共享內(nèi)存:同步是顯式進行的,由于程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執(zhí)行
  • 消息傳遞:同步是隱式進行的,由于消息的發(fā)送必須在消息的接收之前。

總結:

Java的并發(fā)采用的是共享內(nèi)存模型,Java線程之間的通信總是隱式進行,整個通信過程對程序員完全透明,如果編寫多線程程序的Java程序員不理解隱式進行線程之間的通信的工作機制,很可能會遇到各種奇怪的內(nèi)存可見性問題。

1.2 Java內(nèi)存模型的抽象結構

Java中所有的實例域、靜態(tài)域和數(shù)組元素都存儲在堆內(nèi)存中,堆內(nèi)存在線程之間共享(文章中用“共享變量”指代)。局部變量(Local Variables)、方法定義參數(shù)(Formal Method Parameters)和異常處理器參數(shù)(Exception Handler Parameters)不會在線程之間共享,它們不會存在內(nèi)存可見性問題,因此也不受內(nèi)存模型的影響。
Java線程之間的通信由Java內(nèi)存模型(JMM)控制,JMM決定一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內(nèi)存之間的抽象關系:線程之間的共享變量存儲在主內(nèi)存中,每個線程都有一個私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本。本地內(nèi)存時JMM的一個抽象概念,并不真實存在。JMM涵蓋了緩存、寫緩沖區(qū)、寄存器以及其他的硬件和編譯器優(yōu)化。

圖示:Java內(nèi)存模型的抽象示意圖

從上圖來看,線程A和線程B之間要通信的話,必須經(jīng)歷下面2個步驟。

  • 線程A把本地內(nèi)存A中更新過的變量刷新到主內(nèi)存中
  • 線程B到主內(nèi)存中去讀取線程A之前已更新過的共享變量

圖示:線程之間通信示意圖

如上圖所示,本地內(nèi)存A和本地內(nèi)存B有主內(nèi)存中共享變量X的副本。假設初始時,這三個內(nèi)存中的X的值都是0.線程A在執(zhí)行時,把更新后的X的值(假設值為1)臨時存放在自己的本地內(nèi)存A中。當線程A和線程B需要通信是,線程A首先把自己本地內(nèi)存中修改后的X刷新到主內(nèi)存中,此時主內(nèi)存中的X值變?yōu)榱?.隨后,線程B到主內(nèi)存中去讀取線程A更新后的X值,此時線程B的本地內(nèi)存X的值也更新成了1。
從整體來看,這兩個步驟實質(zhì)上是線程A在向線程B發(fā)送消息,而且這個通信過程必須要經(jīng)過主內(nèi)存。JMM通過控制主內(nèi)存與每個線程的本地內(nèi)存之間的交互,來為Java程序員提供內(nèi)存可見性保證。

1.3 從源代碼到指令重排序

在執(zhí)行程序時,為了提高性能,編譯器和處理器常常會對指令做重排序。重排序分為三種類型:

  • 編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執(zhí)行順序。
  • 指令級并行的重排序。現(xiàn)代處理器采用了指令級并行技術(Instruction-Level Parallelism,ILP)來將對跳指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴性,處理器可以改變語句對應及其指令的執(zhí)行順序。
  • 內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫緩沖區(qū),這使得加載和存儲操作看上去可能是在亂序執(zhí)行。

從Java源代碼的最終實際執(zhí)行的指令序列,會分別經(jīng)歷下面3種重排序,其中1屬于編譯器重排序,2和3屬于處理器重排序。

源代碼到最終執(zhí)行的指令序列示意圖:

重排序可能會導致多線程程序出現(xiàn)內(nèi)存可見性問題,對于編譯器,JMM的編譯器重排序規(guī)則會禁止特定類型的編譯器重排序(不是所有的編譯器重排序都需要禁止)。對于處理器重排序,JMM的處理器重排序規(guī)則會要求Java編譯器在生成指令序列時,插入特定類型的內(nèi)存屏障(Memory Barries, Intel稱之為Memory Fence)指令,通過內(nèi)存屏障指令來禁止特定類型的處理器重排序。
JMM屬于語言級的內(nèi)存模型,它確保在不同的編譯器和不同的處理器平臺之上,通過禁止特定類型的編譯器重排序和處理器重排序,為程序員提供一致的內(nèi)存可見性保障。

1.4 寫緩沖區(qū)和內(nèi)存屏障

1.4.1 寫緩沖區(qū)

現(xiàn)代處理器都會使用寫緩沖區(qū)臨時保存向內(nèi)存中寫入的數(shù)據(jù)。寫緩沖區(qū)的主要作用:

  • 可以保證指令流水線持續(xù)運行,可以避免由于處理器停頓下來等待向內(nèi)存寫入數(shù)據(jù)而產(chǎn)生的延遲。
  • 它以批處理的方式方式刷新寫緩沖區(qū),以及合并寫緩沖區(qū)中對統(tǒng)一地址的多次寫,減少對內(nèi)存總線的占用。

常見處理器允許的重排序類型(Y-表示允許兩個操作重排序,N-表示處理器不允許兩個操作重排序)

處理器 \規(guī)則 Load-Load Load-Store Store-Store Store-Load 數(shù)據(jù)依賴性
SPARC-TSO N N N Y N
x86 N N N Y N
IA64 Y Y Y Y N
PowerPC Y Y Y Y N
說明:常見處理器都允許Store-Load重排序;常見的處理器都不允許對存在數(shù)據(jù)依賴性的操作做重排序。N多的表示處理器擁有相對較強的處理器內(nèi)存模型。
由于寫緩沖器僅僅只對它所在的處理器可見,這個特性會對內(nèi)存操作的執(zhí)行順序產(chǎn)生非常重要的影響:處理器對內(nèi)存的讀/寫操作的執(zhí)行順序,不一定與內(nèi)存實際發(fā)生的讀/寫操作順序一致。
舉例說明:
示例項目 \處理器 Processor A Processor B
偽代碼 a=1; //A1x=b;//A2 b=2;//B1y=a;//B2
可能運行結果 初始狀態(tài):a=b=0;處理器允許執(zhí)行后得到結果:x=y=0;
假設處理器A和處理器B按程序的順序并行執(zhí)行內(nèi)存訪問,最終可能得到x=y=0的結果,具體原因如下:

處理器和內(nèi)存交互:

說明:處理器A和處理器B可以同時把共享變量寫入自己的寫緩沖區(qū)(A1、B1),然后從內(nèi)存中讀取另一個共享變量(A2、B2),最后才把自己寫緩沖區(qū)中保存的臟數(shù)據(jù)刷新到內(nèi)存中(A3、B3)。當以這種時序執(zhí)行時,程序就可以得到x=y=0結果。

1.4.2 內(nèi)存屏障

為了保證內(nèi)存可見性,Java編譯器在生成指令序列的適當位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序。

JMM把內(nèi)存屏障指令分為4類:

屏障類型 指令示例 說明
LoadLoad Barriers Load1;LoadLoad;Load2 確保Load1數(shù)據(jù)的裝載先于Load2及所有后續(xù)裝載指令的裝載
StoreStore Barriers Store1;StoreStore;Store2 確保Store1數(shù)據(jù)對其他處理器可見(刷新到主內(nèi)存)先于Store2及所有后續(xù)存儲指令的存儲
LoadStore Barriers Load1;LoadStore;Store2 確保Load1數(shù)據(jù)裝載先于Store2及后續(xù)的存儲指令刷新到內(nèi)存
StoreLoad Barriers**** Store1;StoreLoad;Load2 確保Store1數(shù)據(jù)對其他處理器變得可見(指刷新到主內(nèi)存)先于Load2及所有后續(xù)裝載指令的裝載。StoreLoad Barriers會使該屏障之前的所有內(nèi)存訪問指令(存儲和裝載指令)完成之后,才執(zhí)行屏障之后的內(nèi)存訪問指令。

StoreLoad Barriers是一個“全能型屏障”,它同時具有其它3個屏障的效果?,F(xiàn)代大多數(shù)處理器支持該屏障(其他類型的屏障不一定被所有處理器支持)。執(zhí)行該屏障開銷會很昂貴,因為處理器需要把緩沖區(qū)的內(nèi)容全部刷新到內(nèi)存中(Buffer Fully Flush)。

1.5 happens-before 簡介

從JDK1.5開始,Java使用新的JSR-133內(nèi)存模型。JSR-133使用happens-before的概念來闡述操作之間的內(nèi)存可見性。在JMM中,如果一個操作的結果需要對另一個操作可見,那么這兩個操作之間必須存在happens-before關系。這里的兩個操作可以是單線程也可以是多線程。

happens-before規(guī)則:

  • 程序順序規(guī)則:一個線程中的每個操作,happens-before于該線程的任意后續(xù)操作。
  • 監(jiān)視器鎖規(guī)則:對于一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
  • volatile變量規(guī)則:對于一個volitale域的寫,happens-before于任意后續(xù)對這個volatile域的讀。
  • 傳遞性:如果A happens-before B,且B happens-before C ,那么A happens-before C

注意:

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

圖示happens-before與JMM的關系:

一個happens-before規(guī)則對應于一個或多個編譯器個處理器重排序規(guī)則。對于Java程序員來說,happens-before規(guī)則簡單易懂,它避免了Java程序員為了理解JMM提供的內(nèi)存可見性保證而去學習復雜的重排序規(guī)則以及這些規(guī)則的具體實現(xiàn)方法。

到此這篇關于Java并發(fā)編程之內(nèi)存模型的文章就介紹到這了,更多相關Java內(nèi)存模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JVM 參數(shù)配置詳細介紹

    JVM 參數(shù)配置詳細介紹

    這篇文章主要介紹了JVM 參數(shù)配置詳細介紹的相關資料,需要的朋友可以參考下
    2017-02-02
  • java實現(xiàn)爬蟲爬網(wǎng)站圖片的實例代碼

    java實現(xiàn)爬蟲爬網(wǎng)站圖片的實例代碼

    這篇文章主要介紹了java實現(xiàn)爬蟲爬網(wǎng)站圖片的實例代碼,需要的朋友可以參考下
    2018-06-06
  • JUnit4 Hamcrest匹配器常用方法總結

    JUnit4 Hamcrest匹配器常用方法總結

    這篇文章主要介紹了JUnit4 Hamcrest匹配器常用方法總結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • Springboot 如何使用 SaToken 進行登錄認證、權限管理及路由規(guī)則接口攔截

    Springboot 如何使用 SaToken 進行登錄認證、權限管理及路由規(guī)則接口攔截

    Sa-Token 是一個輕量級 Java 權限認證框架,主要解決:登錄認證、權限認證、單點登錄、OAuth2.0、分布式Session會話、微服務網(wǎng)關鑒權 等一系列權限相關問題,這篇文章主要介紹了Springboot 使用 SaToken 進行登錄認證、權限管理以及路由規(guī)則接口攔截,需要的朋友可以參考下
    2024-06-06
  • Java中List集合去除重復數(shù)據(jù)的方法匯總

    Java中List集合去除重復數(shù)據(jù)的方法匯總

    這篇文章主要給大家介紹了關于Java中List集合去除重復數(shù)據(jù)的方法,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02
  • Java 中This用法的實例詳解

    Java 中This用法的實例詳解

    這篇文章主要介紹了 Java 中This用法的實例詳解的相關資料,希望通過本文大家能理解掌握this關鍵字的使用方法,需要的朋友可以參考下
    2017-09-09
  • Java:

    Java:"失效"的private修飾符

    本文主要介紹Java 失效的private修飾符,這里整理了相關資料說明private 修飾符的作用,如何使用并與C++ 做比較,有興趣的小伙伴可以參考下
    2016-08-08
  • SpringMVC攔截器創(chuàng)建配置及執(zhí)行順序

    SpringMVC攔截器創(chuàng)建配置及執(zhí)行順序

    這篇文章主要為大家介紹了SpringMVC攔截器創(chuàng)建配置及執(zhí)行順序,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • Java 線程池詳解

    Java 線程池詳解

    本文給大家總結了java中的線程池的相關問題,非常的詳細也很實用,有需要的小伙伴可以參考下。
    2016-03-03
  • 解析WeakHashMap與HashMap的區(qū)別詳解

    解析WeakHashMap與HashMap的區(qū)別詳解

    本篇文章是對WeakHashMap與HashMap的區(qū)別進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05

最新評論

遵义县| 惠安县| 利川市| 伊宁市| 老河口市| 神木县| 武功县| 班戈县| 新昌县| 高要市| 揭东县| 常宁市| 靖远县| 五台县| 屏边| 东安县| 即墨市| 宣威市| 渭源县| 庆安县| 荥阳市| 新密市| 邳州市| 扎赉特旗| 宁南县| 中西区| 晋江市| 铁力市| 左云县| 长春市| 广德县| 安义县| 开化县| 和林格尔县| 克拉玛依市| 盘山县| 吐鲁番市| 微博| 志丹县| 仁寿县| 平湖市|