java 并發模型總類_java并發編程系列-內存模型基礎

java線程之間的通信對程序開發人員是完全透明的,內存的可見性問題很容易困擾很多開發人員。本篇博文將揭開java內存模型的神秘面紗,來看看內存模型到底是怎樣的。

并發編程模型的分類

并發編程中需要處理的兩個關鍵問題:線程之間如何通信

線程之間如何同步

所謂通信是指線程之間以何種機制來交換信息,在命令式編程中,線程的通信機制有兩種:共享內存(隱式通信:通過共享程序的公共狀態,讀-寫內存中的公共狀態實現)

消息傳遞(顯示通信:線程間發送消息實現 ,比較典型的就是wait()和notify())

所謂同步,就是控制不同線程間操作發生相對順序的機制:共享內存(同步是顯示的,由程序開發人員顯示的指定某段代碼或者某個方法需要在線程之間互斥執行)。

消息傳遞(同步是隱式的,消息的發送必須在消息接收之前)。

java的并發采用的是共享內存模型,線程之間的通信是隱式執行的,同步需要開發人員顯示進行控制。

JAVA內存模型(JMM)的抽象

JMM把java虛擬機內部劃分為線程棧和堆。邏輯視圖如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

JMM邏輯視圖.png

java中所有的實例域、靜態域,數組元素都是存儲在堆內存,堆內存在線程之間共享。而對象引用,局部變量、方法參數和異常處理器參數都是存在在棧內存,也就是線程棧中,線程棧中的變量僅對自己可見,對其他線程不可見。不同線程之間的通信由java內存模型(java memory model ,簡稱JMM)控制。JMM的抽象結構圖,如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

JMM內存模型抽象圖.png

線程之間的共享變量存儲在堆內存,每個線程都有私有的本地內存(線程棧),私有本地內存中存儲了主內存中共享變量的拷貝,本地內存只是JMM的一個抽象概念,并不真實存在。

上圖中線程A要與線程B通信的話,由于線程本地變量的不可見性,首先要將線程A中變量的更改,刷新到主內存中,然后線程B本地私有的共享變量副本失效,從新讀取刷新的新值,才能完成。從上面的描述看,線程A向線程B通信,必須要經過主內存,JMM控制主內存與每個線程的本地變量的交互,來為java程序員提供內存的可見性。

硬件內存架構

軟件最終還要運行在硬件上,看一下現代計算機硬件內存架構的簡單圖示:

AAffA0nNPuCLAAAAAElFTkSuQmCC

硬件模型.png

現在的計算機一般都有兩個或者多個CPU,其中有些還是多核心實現。

每個CPU都包含一系列的寄存器,它們是CPU內內存的基礎。CPU在寄存器上執行操作的速度遠大于在主存上執行的速度。這是因為CPU訪問寄存器的速度遠大于主存。

每個CPU可能還有一個CPU緩存層。實際上,絕大多數的現代CPU都有一定大小的緩存層。CPU訪問緩存層的速度快于訪問主存的速度,但通常比訪問內部寄存器的速度還要慢一點。一些CPU還有多層緩存,但這些對理解Java內存模型如何和內存交互不是那么重要。只要知道CPU中可以有一個緩存層就可以了。

一個計算機還包含一個主存。所有的CPU都可以訪問主存。主存通常比CPU中的緩存大得多。

CPU的高速緩存雖然解決了效率的問題,但是又帶來了一個新的問題:數據一致性。當一個CPU需要讀取主存時,它會將主存的部分讀到CPU緩存中。它甚至可能將緩存中的部分內容讀到它的內部寄存器中,然后在寄存器中執行操作,這樣就不會使CPU直接與內存相連。當CPU需要將結果寫回到主存中去時,它會將內部寄存器的值刷新到緩存中,然后在某個時間點將值刷新回主存。

Java內存模型和硬件內存架構之間的橋接

上面已經提到,Java內存模型與硬件內存架構之間存在差異。硬件內存架構沒有區分線程棧和堆。對于硬件,所有的線程棧和堆都分布在主內中。部分線程棧和堆可能有時候會出現在CPU緩存中和CPU內部的寄存器中。如下圖所示:

AAffA0nNPuCLAAAAAElFTkSuQmCC

image.png

Java內存模型的基礎原理

從源代碼到指令序列的重排序

在程序執行時,為了提高程序的執行性能,編譯器和處理器常常會對指令做重排序,換句話說程序的執行順序和程序開發人員編寫的順序可能會存在差異,這是編譯器和處理器對源代碼做了優化。但是JMM的編譯器重排序規則會禁止特定類型的編譯器重排序,對于處理器的重排序,JMM的處理器重排序規則會要求java編譯器在生成指令時,插入特定的內存屏障(Memory Barriers)指令,來禁止特定類型的處理器重排序。換句話說編譯器和處理器的重排序都是可控的。

重排序分為三類:編譯器重排序:編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。

指令級重排序:現在的處理器都采用了并行技術,可以將多條執行重疊執行,如果不存在數據依賴性,可以改變語句對應機器語句的執行順序。之所以存在數據依賴的語句不做重排序是因為改變順序后將導致執行結果發生變化。

內存系統重排序:由于處理器使用緩存和讀/寫緩沖區,這使得加載和存儲操作看上去可能是在亂序執行。

JMM屬于語言級的內存模型,它確保在不同的編譯器和不同的處理器平臺上,通過禁止特定類型的編譯器和處理器重排序,為程序員提供一致性的內存可見性保證。

重排序與內存屏障

為了保證內存可見性,Java編譯器在生成指令序列的適當位置會插入內存屏障指令來禁止特定類型的處理器重排序。內存屏障又稱為內存柵欄,是一個CPU指令:保證特定的操作順序

影響某些數據的內存可見性

例如: volatile關鍵字 就是通過內存屏障實現的。

happens-before

JSP-133(內存模型)使用happens-before來闡述操作之間的內存可見性。在JMM中如果一個操作執行結果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關系。兩個操作具有happens-before關系,并不意味著前一個操作必須要在后一個操作之間執行,happens-before僅僅要求前一個操作的執行結果對后一個操作可見。且前一個操作按順序排在第二個操作的前面。

happens-before規則如下:程序順序規則:一個線程中的每個操作,happens-before于線程中任意后續操作

監視器鎖規則:對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖

volatil變量規則:對一個volatile域的寫,happens-before與任意后續對這個volatile域的讀

傳遞性:如果A happens-before B ,且B happens-before C , 那么A happens-before C .

重排序

數據依賴

如果兩個操作訪問同一個變量,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數據的依賴性。寫后讀 a=1;b=a

寫后寫 a=1 ; a =2

讀后寫 a=b ; b =1

上面三種情況,只要重排序兩個操作的執行順序,程序的執行結果就會發生改變。編譯器和處理器可能會對操作做重排序。做重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關系的兩個操作的執行順序,只針對單個處理器和單個線程而言。

as-if-serial語義

它的意思是不管怎么重排序,單線程執行的結果都不會發生變化。為了遵守as-if-serial語義,編譯器和處理器都不會對存在數據依賴的語句執行重排序。

順序一致性

數據競爭與順序一致性保證

當程序未正確同步時,就可能存在數據競爭。在一個線程中寫一個變量

在另一個線程中讀同一個變量

而且讀寫沒有通過同步來排序

順序一致性內存模型

兩大特性:一個線程中所有操作必須按照程序的順序來執行

所有線程都只能看到一個單一的操作執行順序。在順序一致性內存模型中,每個操作都必須原子執行且立刻對所有線程可見。

同步程序的順序一致性效果

同步程序的順序一致性效果將于程序在順序一致性模型中的執行結果相同。

未同步程序的執行特性

對于未同步或未正確同步的多線程程序,JMM只提供最小安全性。JMM不保證未同步的程序的執行結果與該程序在順序一致性模型中的執行結果一致。

作者:起個名忒難

鏈接:https://www.jianshu.com/p/de47a2e49e5d

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/260322.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/260322.shtml
英文地址,請注明出處:http://en.pswp.cn/news/260322.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python調用java的jar包_python調用java的jar包報錯127

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓最近在弄python需要調用到Java的jar包,按照網上的教程走,最后總是報錯No matching overloads found for [init in find. at native\common\jp_method.cpp:127Java:package aes;import com.sun.cr…

iphone、Android接收System.Net.Mail發的郵件標題亂碼

參考地址:http://blog.csdn.net/whowhen21/article/details/5959225 在做項目時候,用到.Net的System.Net.Mail發送郵件,經測試,發現如果標題過長,收到的就會是亂碼了(那種Base64格式的數據),幾經測試&#…

數據倉庫與數據挖掘的一些基本概念

下面內容摘自互聯網并作了整理。 名詞: BI(Business Intelligence):商業智能, DW(Data Warehouse):數據倉庫,詳見正文Q1部分。 OLTP(On-Line Transaction Processing):聯機事務處理 也稱為面向交易的處理系…

ATS讀小文件(內存命中)

一個資源根據其大小可能會存在多個存儲對象中。如果足夠小(連同doc結構的大小小于一個fragment的size),連同這個資源的meta信息一起存儲在一個doc中。如果比較大,第一個存儲對象保存資源的meta信息,后面跟著若干個frag…

python 加密解密_python加密解密

EncodeFile(python2.7加密)# -*- coding: utf8 -*-import base64import sysreload(sys)sys.setdefaultencoding(utf8)inFilesys.argv[1]try:fin open(inFile, "rb")fout open(inFile".txt", "w")base64.encode(fin, fout)passexcept Exception…

java double 兩位_java double 保留兩位小數

java保留兩位小數問題:方式一:四舍五入double f 111231.5585;BigDecimal b new BigDecimal(f);double f1 b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();保留兩位小數---------------------------------------------…

fatal error C1902: 程序數據庫管理器不匹配;請檢查安裝解決

終于找到原因了,原來是我安裝的字體渲染,并且采用注冊表的加載方式!改掉就好了!上天哪,這是怎么影響到的 卸載MacType程序后,進行嘗試! VS2008 和 VS2010 又能用了! 我想求教育。。。…

一分鐘明確 VS manifest 原理

什么是vs 程序的manifest文件 manifest 是VS程序用來標明所依賴的side-by-side組建,如ATL, CRT等的清單。 為什么要有manifest文件 一臺pc上,用一組建往往會有不止一個版本號(c:/windows/winsxs或系統文件夾下),程序在載入的時候&…

[譯]多線程網絡服務模型

2019獨角獸企業重金招聘Python工程師標準>>> 多線程網絡服務模型 /*** 謹獻給Yoyo** 原文出處&#xff1a;https://www.toptal.com/software/guide-to-multi-processing-network-server-models* author dogstar.huang <chanzonghuanggmail.com> 2016-04-02*/作…

likely(x)與unlikely(x)函數,即__builtin_expect的使用

轉載自&#xff1a;http://velep.com/archives/795.html 本文講的likely()和unlikely()兩個宏&#xff0c;在linux內核代碼和一些應用中可常見到它們的身影。實質上&#xff0c;這兩個宏是關于GCC編譯器內置宏__builtin_expect的使用。顧名思義&#xff0c;likely()指“很有可能…

java mvc引擎_SpringMvc+JavaConfig+Idea 搭建項目

1.介紹之前搭建SpringMvc項目要配置一系列的配置文件&#xff0c;比如web.xml,applicationContext.xml,dispatcher.xml。Spring 3.X之后推出了基于JavaConfig方式以及注解的形式的配置。在一定程度上簡化了Spring項目的配置。近幾年特別火的SpringBoot&#xff0c;大大的簡化了…

window.parent和window.opener區別

下面一段代碼是關于window.parent和window.opener區別 來講的&#xff0c;我們如果要用到iframe的值傳到另一框架就要用到window.opener.document.getElementById(name).value uvalue;這種形式哦。 window.parent能獲取一個框架的父窗口或父框架。頂層窗口的parent引用的是它本…

極域電子書包課堂管理系統_【君蓮微訊】君蓮學校(小學部)開展電子書包第13共同體數學研討活動...

借 助 媒 體 技 術豐 富 圖 形 認 識君蓮學校(小學部)開展電子書包共同體 數學研討活動 2020年12月2日下午&#xff0c;君蓮學校(小學部)開展了以“借助媒體技術 豐富圖形認識”為主題的閔行區電子書包第13共同體的數學研討活動。共同體學校教師代表、學校電子書包項目組主管朱…

python批量改動指定文件夾文件名稱

這小樣例僅僅要是說明用python怎么批量改動指定文件夾的文件名稱&#xff1a; 記得要把腳本跟改動的文件放在同一個文件夾下 #encoding:utf-8 import os import sys files os.listdir(D:\\1) #路徑能夠自己for name in files:a os.path.splitext(name)if a[1] .txt: #txt能夠…

Linux vmstat命令實戰詳解

vmstat命令是最常見的Linux/Unix監控工具&#xff0c;可以展現給定時間間隔的服務器的狀態值,包括服務器的CPU使用率&#xff0c;內存使用&#xff0c;虛擬內存交換情況,IO讀寫情況。這個命令是我查看Linux/Unix最喜愛的命令&#xff0c;一個是Linux/Unix都支持&#xff0c;二是…

python的基礎網絡編程是下列_Python入門基礎之網絡編程、socket編程、TCP、UDP編程...

忙了兩天&#xff0c;繼續更文&#xff01;希望多多支持。套接字套接字是一種具有之前所說的"通訊端點"概念的計算機網絡數據結構。網絡化的應用程序在開始任何通訊之前都必需要創建套接字。套接字有三種&#xff1a;1、 AF_UNIX(在 POSIX1.g 標準中也叫 AF_LOCAL)&a…

java 入門 博客園_javaweb入門

復習&#xff1a;css的常用樣式&#xff1a;borderbackgroundpaddingmarginfloatposition 定位top left確定div在頁面中的位置&#xff0c;這兩個值可以為負數。cssdiv 布局方式cssdivtable 先由div劃分大塊兒&#xff0c;再由table進行整齊布局。下拉列表&#xff1a;層疊的布…

以ThreadStart方式實現多線程

3.1 使用ThreadStart委托 這里先以一個例子體現一下多線程帶來的好處&#xff0c;首先在Message類中建立一個方法ShowMessage()&#xff0c;里面顯示了當前運行線程的Id&#xff0c;并使用Thread.Sleep&#xff08;int ) 方法模擬部分工作。在main()中通過ThreadStart委托綁定M…

管理思考

管理基礎 分活 分錢 分責任 分權 安人(安排 配置) 流程 標準 考核 治人(協調 指揮 控制) 社會越來越復雜 分工越來越復雜 合作越來越重要 目標一定要一致共同的意愿共識 需要大家參與管理 業務劃分 責任劃分 流程梳理 如何合作做好安全工作 安全服務因為不承擔責任 責任主體是管…

我的atom插件

atom插件實在是太多了&#xff0c;下面就說說我的插件 1.minimap 右邊的小地圖&#xff0c;和sublime里面的差不多&#xff1b; 2.open-in-browser 右擊默認瀏覽器打開&#xff1b; 3.emmet 這個不用多說吧&#xff0c;html快速編譯 4.git-plus 直接在atom提交代碼&#xff0…