Java內(nèi)存模型可見(jiàn)性的分析,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
創(chuàng)新互聯(lián)公司長(zhǎng)期為上1000家客戶(hù)提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為南通企業(yè)提供專(zhuān)業(yè)的成都做網(wǎng)站、網(wǎng)站設(shè)計(jì),南通網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
給定程序以及一個(gè)檢測(cè)程序是否合法的執(zhí)行跟蹤,JMM工作原理是檢查執(zhí)行跟蹤中的每個(gè)讀,并根據(jù)某些規(guī)則檢查讀觀察到的寫(xiě)是否有效
JMM中可能產(chǎn)生的行為表現(xiàn)為不論代碼是如何實(shí)現(xiàn)程序行為,只要保證程序的所有結(jié)果執(zhí)行和JMM預(yù)期的結(jié)果一致即可
基于上述的第二點(diǎn),對(duì)實(shí)現(xiàn)者執(zhí)行的代碼進(jìn)行轉(zhuǎn)換的實(shí)現(xiàn)就比較自由,可以實(shí)現(xiàn)操作的重排序甚至刪除不必要的同步操作代碼
線(xiàn)程共享與獨(dú)占區(qū)域
線(xiàn)程共享區(qū)域: JVM運(yùn)行數(shù)據(jù)區(qū)中的方法區(qū),堆內(nèi)存存儲(chǔ)的數(shù)據(jù)變量,存在數(shù)據(jù)競(jìng)爭(zhēng),即數(shù)據(jù)讀寫(xiě)的安全問(wèn)題
線(xiàn)程獨(dú)占區(qū)域: JVM為每個(gè)線(xiàn)程單獨(dú)創(chuàng)建的私有區(qū)域,用于存儲(chǔ)當(dāng)前線(xiàn)程私有的數(shù)據(jù)變量,不存在數(shù)據(jù)競(jìng)爭(zhēng),比如線(xiàn)程局部變量,ThreadLocal/ThreadLocalRandom等
線(xiàn)程通信產(chǎn)生數(shù)據(jù)競(jìng)爭(zhēng)
簡(jiǎn)要的源代碼
// constant.java
final int P = 10;
final int C = 20;
// shared.java
int pwrite = 0;
int cwrite = 0;
// producer.java
int pread = 0;
public void run(){
pread = cwrite; // 生產(chǎn)者線(xiàn)程需要消費(fèi)者線(xiàn)程cwrite的數(shù)據(jù)
pwrite = P;
}
// consumer.java
int cread = 0;
public void run(){
cread = pwrite;// 消費(fèi)者線(xiàn)程需要生產(chǎn)者線(xiàn)程的pwrite數(shù)據(jù)
cwrite = C;
}
//按正常結(jié)果輸出的預(yù)期值推斷,不會(huì)產(chǎn)生同時(shí)pread == C(20)和cread == P(10)
結(jié)果反推分析(基于我們看到的代碼順序)
如果上述的執(zhí)行結(jié)果成立,那么cwrite = C
一定是在pread = cwrite
之前執(zhí)行的;
由于cwrite = C
是在cread = pwrite
之后執(zhí)行,所以cread = pwrite
一定是在pread = cwrite
之前執(zhí)行的;
也就是cread = pwrite
一定是在pwrite = P
之前執(zhí)行的,所以結(jié)果是不成立
產(chǎn)生問(wèn)題
線(xiàn)程既然存在寫(xiě)操作,那么寫(xiě)操作的數(shù)據(jù)變量一定會(huì)讓另一個(gè)線(xiàn)程讀取到對(duì)應(yīng)寫(xiě)后的數(shù)據(jù)么?
由于線(xiàn)程本身也有自己的工作內(nèi)存,因此讀取數(shù)據(jù)變量不一定就是另一個(gè)線(xiàn)程寫(xiě)操作之后的數(shù)據(jù),此時(shí)可能讀取到工作內(nèi)存上的緩存數(shù)據(jù)(臟數(shù)據(jù))
同時(shí)基于JMM規(guī)范,產(chǎn)生優(yōu)化后可能執(zhí)行的代碼
public void run(){
pread = cwrite; // 生產(chǎn)者線(xiàn)程需要消費(fèi)者線(xiàn)程cwrite的數(shù)據(jù) --1
pwrite = P; // --2
}
// consumer.java
int cread = 0;
public void run(){
cwrite = C; // --3
cread = pwrite;// 消費(fèi)者線(xiàn)程需要生產(chǎn)者線(xiàn)程的pwrite數(shù)據(jù) -4
}
在上述兩個(gè)線(xiàn)程中分析
線(xiàn)程在并發(fā)下,可能產(chǎn)生執(zhí)行的順序?yàn)?-1-2-4,也就是同時(shí)產(chǎn)生pread == C(20)和cread == P(10)
數(shù)據(jù)競(jìng)爭(zhēng)
當(dāng)前線(xiàn)程對(duì)一個(gè)變量執(zhí)行寫(xiě)操作
同時(shí)另一個(gè)線(xiàn)程對(duì)相同的變量執(zhí)行讀操作
讀寫(xiě)操作沒(méi)有通過(guò)同步實(shí)現(xiàn)排序
產(chǎn)生問(wèn)題
不同線(xiàn)程之間通信會(huì)對(duì)共享變量的數(shù)據(jù)產(chǎn)生競(jìng)爭(zhēng),在這種情況下,JMM作出重排序的優(yōu)化會(huì)導(dǎo)致輸出結(jié)果與預(yù)期的結(jié)果不一致,如果放在實(shí)際的業(yè)務(wù)場(chǎng)景中,將會(huì)導(dǎo)致很多無(wú)法控制的業(yè)務(wù)邏輯錯(cuò)誤,后果不可想象.
JMM下的并發(fā)問(wèn)題
其一,讀取到的共享數(shù)據(jù)不一定是寫(xiě)操作之后的數(shù)據(jù),也就是寫(xiě)操作對(duì)讀操作不可見(jiàn)(緩存導(dǎo)致)
其二,JMM為了提升性能對(duì)代碼進(jìn)行重排序,那么就會(huì)導(dǎo)致數(shù)據(jù)產(chǎn)生的結(jié)果和預(yù)期的不一致(重排序?qū)е?
線(xiàn)程之工作內(nèi)存
JMM抽象之工作內(nèi)存(線(xiàn)程本地內(nèi)存)
線(xiàn)程棧中的存儲(chǔ)的變量,如局部變量,方法參數(shù),異常處理參數(shù)等
CPU高速緩存
線(xiàn)程,工作內(nèi)存,JMM與主內(nèi)存
從上述可知,在JVM運(yùn)行數(shù)據(jù)區(qū)中,工作內(nèi)存與主內(nèi)存是通過(guò)JMM模型規(guī)范來(lái)完成彼此之間的數(shù)據(jù)交互,因此可以通過(guò)JMM定義的內(nèi)存語(yǔ)義規(guī)范來(lái)提供數(shù)據(jù)變量的可見(jiàn)性
基于緩存問(wèn)題解決方案
JMM規(guī)范規(guī)定使用針對(duì)的技術(shù)手段時(shí),將強(qiáng)制線(xiàn)程直接繞過(guò)工作內(nèi)存讀取主內(nèi)存的共享數(shù)據(jù)
常用技術(shù)手段:volatile/synchronized/final/具有內(nèi)存同步的操作指令
重排序
遵循規(guī)則
as-if-serial: 即不管怎么進(jìn)行重排序(編譯器和處理器為了提高并行度),(單線(xiàn)程)程序的執(zhí)行結(jié)果不能被改變.編譯器/runtime/處理器都必須遵循as-if-serial語(yǔ)義,也就是說(shuō)編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴(lài)關(guān)系的操作做重排序
重排序的分類(lèi)
編譯器重排序: 基于單個(gè)線(xiàn)程程序的語(yǔ)義前提下,Java開(kāi)啟server模式(clinet不支持)可以對(duì)程序代碼進(jìn)行編譯優(yōu)化
處理器重排序:在沒(méi)有存在數(shù)據(jù)依賴(lài)的前提下,處理器可以改變機(jī)器指令的執(zhí)行順序
重排序解決方案
編譯器會(huì)根據(jù)JMM特定類(lèi)型(同步代碼標(biāo)志等)禁止進(jìn)行重排序
在Java編譯器生成指令之前插入特定的內(nèi)存屏障來(lái)禁止處理器重排序
內(nèi)存屏障類(lèi)型: 參見(jiàn)CPU高速緩存與內(nèi)存屏障
關(guān)于Java內(nèi)存模型可見(jiàn)性的分析問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
網(wǎng)站題目:Java內(nèi)存模型可見(jiàn)性的分析
網(wǎng)頁(yè)網(wǎng)址:http://chinadenli.net/article42/gieohc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、ChatGPT、關(guān)鍵詞優(yōu)化、靜態(tài)網(wǎng)站、品牌網(wǎng)站制作、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)