淺談一下Java中的內(nèi)存模型JMM
一、什么是 JMM
JMM,全程是 Java Memory Model ,直譯就是 Java 內(nèi)存模型。根據(jù)這個(gè)名字,可以知道它是 Java 設(shè)計(jì)用來管理內(nèi)存的一個(gè)模型。
Java 中的內(nèi)存分為主內(nèi)存和本地內(nèi)存,線程之間的共享變量存儲(chǔ)在主內(nèi)存(Main Memory)中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。下圖很清晰地闡述了這個(gè)關(guān)系 :

因?yàn)?Java 通過共享內(nèi)存作為線程間的通信機(jī)制,因此 JMM 和線程通信息息相關(guān),可以說 JMM 控制線程的通信,具體來說 JMM 控制一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見 。
二、 JMM 基礎(chǔ)
本節(jié)介紹了解 JMM 需要的一些前置知識(shí), 即和 JMM 有關(guān)的一些概念
2.1 Java 線程通信機(jī)制
線程通信機(jī)制一共有兩種(共享內(nèi)存 和 消息傳遞), Java 使用 共享內(nèi)存模型 作為線程同步機(jī)制
2.2 同步
同步是指程序如何控制不同線程操作發(fā)生的相對(duì)順序,在多線程并發(fā)情況下,同步是保證多線程下安全問題的非常重要的操作。
2.3 堆內(nèi)存
Java 內(nèi)存有棧內(nèi)存、堆內(nèi)存 和 方法區(qū) 。 堆內(nèi)存中含有線程共享的 實(shí)例、靜態(tài)變量 和 數(shù)組元素等 ,也就是說堆內(nèi)存是線程共享區(qū) 。
2.4 指令重排
正常情況,按照我們的理解,我們寫一段程序在處理器中就是按照我們寫的順序執(zhí)行的 。
但是實(shí)際上,在我們看不到的地方,處理器為了提高執(zhí)行效率,優(yōu)化性能,會(huì)對(duì)指令進(jìn)行重排 。
以下面的程序?yàn)槔?,解釋指令重排?/p>
int a = 1; // .... 語句1 int b = 2; // ..... 語句 2
正常情況下, 語句 1 優(yōu)先于 語句 2 執(zhí)行 。
但是,處理器實(shí)際在執(zhí)行時(shí),會(huì)根據(jù)情況進(jìn)行處理,可能會(huì)出現(xiàn) 語句2 優(yōu)先與 語句1 執(zhí)行的情況。
但是因?yàn)閷?duì)程序的執(zhí)行結(jié)果沒有影響, 所以,我們程序員不知道發(fā)生了指令重排 。
在單線程情況下,指令重排不影響程序運(yùn)行的結(jié)果。但是多線程情況下,可能會(huì)造成影響。
因此, JMM 通過插入 內(nèi)存屏障 禁止特定類型的處理器重排序來保證多線程并發(fā)下的程序執(zhí)行安全 。
2.5 順序一致性模型
順序一致性模型,是 JMM 為保證多線程下的安全而提出的,正確同步的程序應(yīng)該具有順序一致性,其具有如下特點(diǎn):
按照程序順序執(zhí)行,不能重排序操作原子執(zhí)行,對(duì)所有線程可見 2.6 同步程序
同步程序并不是完全按照順序一致性執(zhí)行,其在臨界區(qū)內(nèi)可以進(jìn)行重排序,這樣的設(shè)計(jì)給了處理器足夠的優(yōu)化空間,同時(shí)也沒有改變程序的執(zhí)行結(jié)果 。
三、volatile 關(guān)鍵字
3.1 volatile 做什么
volatile 相當(dāng)于同一個(gè)鎖對(duì)對(duì)該變量的讀寫操作進(jìn)行了同步,用 volatile 修飾的變量總是能被同步到共享內(nèi)存中,避免了因?yàn)楸镜貎?nèi)存的存在導(dǎo)致的線程不安全問題 。volatile 具有如下兩個(gè)特性:
- 可見性:一個(gè)線程的讀,總是能看到另一個(gè)線程的寫
- 原子性: 對(duì)單個(gè)變量的讀寫具有原子性, 但是對(duì)復(fù)合操作不具有原子性
3.2 volatile 原理
3.2.1 規(guī)則
- 當(dāng)?shù)诙€(gè)操作是volatile寫時(shí),不管第一個(gè)操作是什么,都不能重排序
- 當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序
- 當(dāng)?shù)谝粋€(gè)操作是volatile寫,第二個(gè)操作是volatile讀時(shí),不能重排序
3.2.2 實(shí)現(xiàn)
生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序
四、鎖的內(nèi)存語義
- 鎖釋放與volatile寫有相同的內(nèi)存語義
- 鎖獲取與volatile讀有相同的內(nèi)存語義
4.1 AQS Java同步器框架
AQS使用一個(gè)整型的volatile變量來維護(hù)同步狀態(tài), 實(shí)現(xiàn) ReentrantLock
4.2 concurrent包
concurrent 包依賴于 CAS 和 volatile 實(shí)現(xiàn)
- 聲明共享變量為volatile
- 使用CAS的原子條件更新來實(shí)現(xiàn)線程之間的同步
- 配合以 volatile 的讀/寫和 CAS 所具有的 volatile 讀和寫的內(nèi)存語義來實(shí)現(xiàn)線程之間的通信
到此這篇關(guān)于淺談一下Java中的內(nèi)存模型JMM的文章就介紹到這了,更多相關(guān)Java內(nèi)存模型JMM內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot?集成?Quartz并使用Cron?表達(dá)式實(shí)現(xiàn)定時(shí)任務(wù)
本篇文章介紹了如何在?Spring?Boot?中集成?Quartz?進(jìn)行定時(shí)任務(wù)調(diào)度,并通過?Cron?表達(dá)式?控制任務(wù)執(zhí)行時(shí)間,Quartz?提供了更強(qiáng)大的任務(wù)調(diào)度能力,比?@Scheduled?注解更靈活,適用于復(fù)雜的定時(shí)任務(wù)需求2025-04-04
SpringBoot數(shù)據(jù)訪問的實(shí)現(xiàn)
本文主要介紹了SpringBoot數(shù)據(jù)訪問的實(shí)現(xiàn),引入各種xxxTemplate,xxxRepository來簡化我們對(duì)數(shù)據(jù)訪問層的操作,感興趣的可以了解一下2023-11-11
druid多數(shù)據(jù)源配置+Datasurce動(dòng)態(tài)切換方式
這篇文章主要介紹了druid多數(shù)據(jù)源配置+Datasurce動(dòng)態(tài)切換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
springboot使用jasypt加密庫實(shí)現(xiàn)數(shù)據(jù)庫加解密示例代碼
這篇文章主要給大家介紹了關(guān)于springboot使用jasypt加密庫實(shí)現(xiàn)數(shù)據(jù)庫加解密的相關(guān)資料,Jasypt是一個(gè)用于配置文件加密的Java庫,它可以用來加密和解密配置文件中的敏感信息,如數(shù)據(jù)庫密碼、API?密鑰等,需要的朋友可以參考下2024-04-04
Java的字符讀寫類CharArrayReader和CharArrayWriter使用示例
這篇文章主要介紹了Java的字符讀寫類CharArrayReader和CharArrayWriter使用示例,兩個(gè)類分別繼承于Reader和Writer,需要的朋友可以參考下2016-06-06
Java通過反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值
這篇文章主要介紹了Java通過反射,動(dòng)態(tài)修改注解的某個(gè)屬性值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Mybatis CURD及模糊查詢功能的實(shí)現(xiàn)
這篇文章主要介紹了Mybatis CURD及模糊查詢功能的實(shí)現(xiàn),有查詢刪除,插入,更新功能,通過實(shí)例代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-06-06

