溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java內(nèi)存模型可見性的分析

發(fā)布時(shí)間:2021-10-21 13:59:16 來源:億速云 閱讀:148 作者:柒染 欄目:大數(shù)據(jù)

Java內(nèi)存模型可見性的分析,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。

1. JMM模型描述

  • 給定程序以及一個(gè)檢測(cè)程序是否合法的執(zhí)行跟蹤,JMM工作原理是檢查執(zhí)行跟蹤中的每個(gè)讀,并根據(jù)某些規(guī)則檢查讀觀察到的寫是否有效

  • 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)操作的重排序甚至刪除不必要的同步操作代碼

2. JMM之?dāng)?shù)據(jù)共享與競(jìng)爭(zhēng)

線程共享與獨(dú)占區(qū)域

  • 線程共享區(qū)域: JVM運(yùn)行數(shù)據(jù)區(qū)中的方法區(qū),堆內(nèi)存存儲(chǔ)的數(shù)據(jù)變量,存在數(shù)據(jù)競(jìng)爭(zhēng),即數(shù)據(jù)讀寫的安全問題

  • 線程獨(dú)占區(qū)域: JVM為每個(gè)線程單獨(dú)創(chuàng)建的私有區(qū)域,用于存儲(chǔ)當(dāng)前線程私有的數(shù)據(jù)變量,不存在數(shù)據(jù)競(jìng)爭(zhēng),比如線程局部變量,ThreadLocal/ThreadLocalRandom等

Java內(nèi)存模型可見性的分析

線程通信產(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)者線程需要消費(fèi)者線程cwrite的數(shù)據(jù)
pwrite = P;
}
// consumer.java
int cread = 0;
public void run(){
cread = pwrite;// 消費(fèi)者線程需要生產(chǎ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)生問題

    • 線程既然存在寫操作,那么寫操作的數(shù)據(jù)變量一定會(huì)讓另一個(gè)線程讀取到對(duì)應(yīng)寫后的數(shù)據(jù)么?

    • 由于線程本身也有自己的工作內(nèi)存,因此讀取數(shù)據(jù)變量不一定就是另一個(gè)線程寫操作之后的數(shù)據(jù),此時(shí)可能讀取到工作內(nèi)存上的緩存數(shù)據(jù)(臟數(shù)據(jù))

  • 同時(shí)基于JMM規(guī)范,產(chǎn)生優(yōu)化后可能執(zhí)行的代碼

public void run(){
pread = cwrite; // 生產(chǎn)者線程需要消費(fèi)者線程cwrite的數(shù)據(jù)  --1
pwrite = P;     // --2
}
// consumer.java
int cread = 0;
public void run(){
cwrite = C;     // --3
cread = pwrite;// 消費(fèi)者線程需要生產(chǎn)者線程的pwrite數(shù)據(jù) -4
}
  • 在上述兩個(gè)線程中分析

    • 線程在并發(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)前線程對(duì)一個(gè)變量執(zhí)行寫操作

    • 同時(shí)另一個(gè)線程對(duì)相同的變量執(zhí)行讀操作

    • 讀寫操作沒有通過同步實(shí)現(xiàn)排序

  • 產(chǎ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ā)問題

  • 其一,讀取到的共享數(shù)據(jù)不一定是寫操作之后的數(shù)據(jù),也就是寫操作對(duì)讀操作不可見(緩存導(dǎo)致)

  • 其二,JMM為了提升性能對(duì)代碼進(jìn)行重排序,那么就會(huì)導(dǎo)致數(shù)據(jù)產(chǎn)生的結(jié)果和預(yù)期的不一致(重排序?qū)е?

3. JMM可見性解決方案

線程之工作內(nèi)存

  • JMM抽象之工作內(nèi)存(線程本地內(nèi)存)

    • 線程棧中的存儲(chǔ)的變量,如局部變量,方法參數(shù),異常處理參數(shù)等

    • CPU高速緩存

  • 線程,工作內(nèi)存,JMM與主內(nèi)存

Java內(nèi)存模型可見性的分析從上述可知,在JVM運(yùn)行數(shù)據(jù)區(qū)中,工作內(nèi)存與主內(nèi)存是通過JMM模型規(guī)范來完成彼此之間的數(shù)據(jù)交互,因此可以通過JMM定義的內(nèi)存語(yǔ)義規(guī)范來提供數(shù)據(jù)變量的可見性

  • 基于緩存問題解決方案

    • JMM規(guī)范規(guī)定使用針對(duì)的技術(shù)手段時(shí),將強(qiáng)制線程直接繞過工作內(nèi)存讀取主內(nèi)存的共享數(shù)據(jù)

    • 常用技術(shù)手段:volatile/synchronized/final/具有內(nèi)存同步的操作指令

重排序

  • 遵循規(guī)則

    • as-if-serial: 即不管怎么進(jìn)行重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變.編譯器/runtime/處理器都必須遵循as-if-serial語(yǔ)義,也就是說編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序

  • 重排序的分類

    • 編譯器重排序: 基于單個(gè)線程程序的語(yǔ)義前提下,Java開啟server模式(clinet不支持)可以對(duì)程序代碼進(jìn)行編譯優(yōu)化

    • 處理器重排序:在沒有存在數(shù)據(jù)依賴的前提下,處理器可以改變機(jī)器指令的執(zhí)行順序

  • 重排序解決方案

    • 編譯器會(huì)根據(jù)JMM特定類型(同步代碼標(biāo)志等)禁止進(jìn)行重排序

    • 在Java編譯器生成指令之前插入特定的內(nèi)存屏障來禁止處理器重排序

  • 內(nèi)存屏障類型: 參見CPU高速緩存與內(nèi)存屏障

關(guān)于Java內(nèi)存模型可見性的分析問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI