3.Java內(nèi)存模型

Java并發(fā)編程的藝術(shù)筆記


目錄

  • 內(nèi)存模型基礎(chǔ)
  • volatile的內(nèi)存語義
  • 鎖的內(nèi)存語義
  • final域的內(nèi)存語義
  • happens-before
  • 雙重檢查鎖定與延遲初始化
  • Java內(nèi)存模型綜述
  • 小結(jié)

內(nèi)存模型基礎(chǔ)

1、并發(fā)編程的兩個(gè)關(guān)鍵問題
  • 線程之間如何通信?
    通信是指 以何種機(jī)制來交換信息。
    命令式編程中線程的通信機(jī)制主要是以下兩種:

    • 共享內(nèi)存 的并發(fā)模型:通過 讀寫內(nèi)存中的公共狀態(tài) 來進(jìn)行隱式通信。
    • 消息傳遞 的并發(fā)模型:沒有公共狀態(tài),只能 通過發(fā)送消息來顯示的進(jìn)行通信。
  • 線程之間如何同步?
    同步是指 程序中用于控制不同線程間操作發(fā)生相對(duì)順序 的機(jī)制。

    • 共享內(nèi)存 的并發(fā)模型:同步時(shí)顯示進(jìn)行的。我們必須顯示指定某段代碼需要在線程直線互斥執(zhí)行。
    • 消息傳遞 的并發(fā)模型:由于消息發(fā)送必須在消息接收之前,因此同步時(shí)隱式的。

Java并發(fā) 采用的是 共享內(nèi)存模型,Java線程之前的通信總是隱式進(jìn)行的。

2、Java內(nèi)存模型的抽象結(jié)構(gòu)

在Java中,所有 實(shí)例域、靜態(tài)域數(shù)組元素 都儲(chǔ)存在堆內(nèi)存中,堆內(nèi)存在線程之前共享。
本文用 共享變量 統(tǒng)一描述 實(shí)例域、靜態(tài)域 和 數(shù)組元素 。

局部變量方法定義參數(shù)、異常處理器參數(shù) 不會(huì)在內(nèi)存之間共享,他們不會(huì)有內(nèi)存可見性問題,也不受內(nèi)存模型影響。

Java線程通信由Java內(nèi)存模型(簡(jiǎn)稱 JMM)控制,JMM 決定一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見。
從抽象角度看,JMM定義了 線程主內(nèi)存 之間的抽象關(guān)系:線程之間的共享變量?jī)?chǔ)存在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存儲(chǔ)存了 該線程 以讀寫共享變量的副本。

Java內(nèi)存模型抽象結(jié)構(gòu)示意圖

從上圖來看,線程A和線程B需要通信的話,需要經(jīng)歷以下步驟:
1、線程A 把 本地內(nèi)存A 中的 共享變量副本 刷新到 主內(nèi)存 中。
2、線程B 去讀取 主內(nèi)存 中 線程A 刷新過的 共享變量。

從整體來看,這兩個(gè)步驟實(shí)質(zhì)上是線程A向線程B發(fā)送消息,而通信必須經(jīng)過主內(nèi)存。
JMM 通過控制主內(nèi)存與每個(gè)線程的本地內(nèi)存之間的交互,來提供內(nèi)存可見性的保證。

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

執(zhí)行程序的時(shí)候,為了提高性能,編譯器處理器 常常會(huì)對(duì)指令做 重排序。
主要有以下三類:

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

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


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

上圖中的 1 屬于 編譯器重排序,2 和 3 屬于 處理器重排序。這些重排序可能會(huì)導(dǎo)致多線程程序出現(xiàn)內(nèi)存可見性問題。

對(duì)于編譯器重排序, JMM的編譯器重排序規(guī)則 會(huì)禁止特定類型的編譯器重排序。
對(duì)于處理器重排序,JMM的處理器重排序規(guī)則 會(huì)要求編譯器在生成指令序列時(shí),插入特定類型的內(nèi)存屏障指令,通過內(nèi)存屏障指令來禁止特定類型的處理器重排序。

4、happens-before簡(jiǎn)介

JDK5 開始,Java使用新的 JSR-133 內(nèi)存模型。 JSR-133 使用 happens-before 的概念來闡述操作之間的內(nèi)存可見性。在 JMM 中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見,則這兩個(gè)操作必須要存在happen-before關(guān)系 。

happen-before 規(guī)則如下:

  • 程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happen-before與該線程中的任意后續(xù)操作
  • 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖,happen-before與隨后這個(gè)鎖的加鎖
  • volatile變量規(guī)則:對(duì)于一個(gè)volatile域的寫,happen-before與任意后續(xù)對(duì)這個(gè)volatile域的讀
  • 傳遞性: A happen-before B,B happen-before C,則A happen-before C

volatile的內(nèi)存語義

理解volatile特性的一個(gè)好方法是把對(duì)volatile變量的單個(gè)讀/寫,看成是 使用同一個(gè)鎖 對(duì)這些單個(gè)讀/寫操作做了同步。

volatile變量具有下列特性:

  • 可見性:總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入。
  • 原子性:對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但類似于volatile++這種復(fù)合操作不具有原子性。

volatile寫的內(nèi)存語義:當(dāng)寫一個(gè)volatile變量時(shí),JMM 會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。
volatile讀的內(nèi)存語義:當(dāng)讀一個(gè)volatile變量時(shí),JMM 會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量。

volatile內(nèi)存語義的實(shí)現(xiàn):

為了實(shí)現(xiàn)volatile內(nèi)存語義,JMM 會(huì)分別限制這兩種類型的重排序類型。

以下是 JMM 針對(duì) 編譯器 制定的 volatile重排序規(guī)則表:

是否能重排序 第二個(gè)操作 第二個(gè)操作 第二個(gè)操作
第一個(gè)操作 普通讀寫 volatile讀 volatile寫
普通讀寫 (1)NO
volatile讀 NO NO NO
volatile寫 NO NO

第三行最后一個(gè)單元格(1)的意思是:在程序中,當(dāng)?shù)谝粋€(gè)操作為普通變量的讀或?qū)憰r(shí),如果第二個(gè)操作為volatile寫,則編譯器不能重排序這兩個(gè)操作。
在上表中,我們可以知道:

  • 當(dāng)?shù)诙€(gè)操作是volatile 寫時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile 寫之前的操作不會(huì)被編譯器重排序到volatile 寫之后。
  • 當(dāng)?shù)谝粋€(gè)操作是volatile 讀時(shí),不管第二個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile 讀之后的操作不會(huì)被編譯器重排序到volatile 讀之前。
  • 當(dāng)?shù)谝粋€(gè)操作是volatile 寫,第二個(gè)操作是volatile 讀時(shí),不能重排序。

為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入 內(nèi)存屏障 來禁止特定類型的 處理器重排序。
對(duì)于編譯器來說,發(fā)現(xiàn)一個(gè)最優(yōu)布置來最小化插入屏障的總數(shù)幾乎不可能。
為此,JMM采取保守策略。
下面是基于保守策略的JMM內(nèi)存屏障插入策略。

  • 在每個(gè)volatile 寫操作的前面插入一個(gè)StoreStore屏障,后面插入一個(gè)StoreLoad屏障。
  • 在每個(gè)volatile 讀操作的后面插入一個(gè)LoadLoad屏障,后面插入一個(gè)LoadStore屏障。

當(dāng)讀線程的數(shù)量大大超過寫線程時(shí),選擇在volatile寫之后插入StoreLoad屏障將帶來可觀的執(zhí)行效率的提升。


鎖的內(nèi)存語義

鎖是Java并發(fā)編程中最重要的同步機(jī)制。鎖除了讓臨界區(qū)互斥執(zhí)行外,還可以讓釋放鎖的線程向獲取同一個(gè)鎖的線程發(fā)送消息。

當(dāng)線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中。和 volatile 寫 類似。
當(dāng)線程獲取鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。和 volatile 讀 類似。

鎖釋放和鎖獲取的內(nèi)存語義總結(jié):

  • 線程A釋放一個(gè)鎖,實(shí)質(zhì)上是線程A向接下來將要獲取這個(gè)鎖的某個(gè)線程發(fā)出了(線程A對(duì)共享變量所做修改的)消息。
  • 線程B獲取一個(gè)鎖,實(shí)質(zhì)上是線程B接收了之前某個(gè)線程發(fā)出的(在釋放這個(gè)鎖之前對(duì)共享變量所做修改的)消息。
  • 線程A釋放鎖,隨后線程B獲取這個(gè)鎖,這個(gè)過程實(shí)質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息。

類似 Java并發(fā)編程基礎(chǔ) 介紹的 等待/通知 機(jī)制。

鎖的介紹


final域的內(nèi)存語義

與前面介紹的鎖和volatile相比,對(duì)final域的讀和寫更像是普通的變量訪問。

對(duì)于final域,編譯器處理器 要遵守兩個(gè) 重排序規(guī)則。

  • 在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final域的寫入,與隨后把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序。
  • 初次讀一個(gè)包含final域的對(duì)象的引用,與隨后初次讀這個(gè)final域,這兩個(gè)操作之間不能重排序。

final域 的重排序規(guī)則禁止把final 域的寫重排序到構(gòu)造函數(shù)之外。

final域 的重排序規(guī)則是,在一個(gè)線程中,初次讀對(duì)象引用與初次讀該對(duì)象包含的 final域,JMM禁止處理器重排序這兩個(gè)操作(注意,這個(gè)規(guī)則僅僅針對(duì)處理器)。


happens-before

happens-beforeJMM 最核心的概念。

重排序規(guī)則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行。

happens-before關(guān)系的定義如下:

  • 如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。
  • 兩個(gè)操作之間存在happens-before關(guān)系,并不意味著Java平臺(tái)的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序并不非法(也就是說,JMM允許這種重排序)。

《JSR-133:Java Memory Model and Thread Specification》 定義了如下happens-before規(guī)則:

  • 程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happens-before于該線程中的任意后續(xù)操作。
  • 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖。
  • volatile變量規(guī)則:對(duì)一個(gè)volatile 域的寫,happens-before于任意后續(xù)對(duì)這個(gè)volatile 域的讀。
  • 傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
  • start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動(dòng)線程B),那么A線程ThreadB.start()操作happens-before線程B中的任意操作。
  • join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before線程AThreadB.join()操作成功返回。

雙重檢查鎖定與延遲初始化

雙重檢查鎖定 示例代碼:

private static Instance instance; //1
public static Instance getInstance() { //2
    if (instance == null) { //3
        synchronized (Instance.class) { //4
            if (instance == null) { //5
                instance = new Instance() //6
            }
        }
    }
    return instance;
}

存在的問題
在線程執(zhí)行到第3if (instance == null),代碼讀取到instance不為null時(shí),instance引用的對(duì)象有可能還沒有完成初始化。

問題的根源
前面的雙重檢查鎖定示例代碼的第6instance=new Singleton();創(chuàng)建了一個(gè)對(duì)象。
這一行可以分解為:

memory = allocate();  // 1:分配對(duì)象的內(nèi)存空間
ctorInstance(memory);  // 2:初始化對(duì)象
instance = memory;   // 3:設(shè)置instance指向剛分配的內(nèi)存地址

代碼中的23之間,可能會(huì)被重排序?yàn)椋?/p>

memory = allocate();  // 1:分配對(duì)象的內(nèi)存空間
instance = memory;   // 3:設(shè)置instance指向剛分配的內(nèi)存地址
// 注意,此時(shí)對(duì)象還沒有被初始化!
ctorInstance(memory);  // 2:初始化對(duì)象

如下圖所示,只要保證2排在4的前面,即使2和3之間重排序了,也不會(huì)違反intra-thread semantics。

線程執(zhí)行時(shí)序圖

如果發(fā)生重排序,另一個(gè)并發(fā)執(zhí)行的線程B就有可能在示例代碼第 3if (instance == null)判斷instance不為null。

解決方法

  • 不允許圖中 23 重排序。
  • 允許圖中 23 重排序,但不允許其他線程“看到”這個(gè)重排序。

基于volatile的解決方案
只需要給變量 instance 添加 volatile 修飾符。

private volatile static Instance instance;
public static Instance getInstance() {
    if (instance == null) {
        synchronized (Instance.class) {
            if (instance == null) {
                instance = new Instance();
            }
        }
    }
    return instance;
}

基于類初始化的解決方案

public static class InstanceFactory {
    public static Instance getInstance() {
        // 這里將導(dǎo)致InstanceHolder類被初始化
        return InstanceHolder.instance;
    }
    private static class InstanceHolder {
        public static Instance instance = new Instance();
    }
}

初始化一個(gè)類,包括執(zhí)行這個(gè)類的靜態(tài)初始化和初始化在這個(gè)類中聲明的靜態(tài)字段。根據(jù)Java語言規(guī)范,在首次發(fā)生下列任意一種情況時(shí),一個(gè)類或接口類型T將被立即初始化。

  • T是一個(gè)類,而且一個(gè)T類型的實(shí)例被創(chuàng)建。
  • T是一個(gè)類,且T中聲明的一個(gè)靜態(tài)方法被調(diào)用。
  • T中聲明的一個(gè)靜態(tài)字段被賦值。
  • T中聲明的一個(gè)靜態(tài)字段被使用,而且這個(gè)字段不是一個(gè)常量字段。
  • T是一個(gè)頂級(jí)類(Top Level Class,見Java語言規(guī)范的§7.6),而且一個(gè)斷言語句嵌套在T內(nèi)部被執(zhí)行。

InstanceFactory示例代碼中,首次執(zhí)行getInstance()方法的線程將導(dǎo)致InstanceHolder類被初始化(符合第4條)。

由于Java語言是多線程的,多個(gè)線程可能在同一時(shí)間嘗試去初始化同一個(gè)類或接口。
因此,在Java中初始化一個(gè)類或者接口時(shí),需要做細(xì)致的同步處理。

Java語言規(guī)范規(guī)定,對(duì)于每一個(gè)類或接口C,都有一個(gè)唯一的初始化鎖LC與之對(duì)應(yīng)。從CLC的映射,由JVM的具體實(shí)現(xiàn)去自由實(shí)現(xiàn)。JVM在類初始化期間會(huì)獲取這個(gè)初始化鎖,并且每個(gè)線程至少獲取一次鎖來確保這個(gè)類已經(jīng)被初始化過了。


Java內(nèi)存模型綜述

處理器的內(nèi)存模型

順序一致性內(nèi)存模型 是一個(gè) 理論參考模型,JMM和處理器內(nèi)存模型在設(shè)計(jì)時(shí)通常會(huì)以順序一致性內(nèi)存模型為參照。
在設(shè)計(jì)時(shí),JMM和處理器內(nèi)存模型會(huì) 對(duì)順序一致性模型做一些放松,因?yàn)槿绻耆凑枕樞蛞恢滦阅P蛠韺?shí)現(xiàn)處理器和JMM,那么很多的處理器和編譯器優(yōu)化都要被禁止,這對(duì)執(zhí)行性能將會(huì)有很大的影響。

根據(jù)對(duì)不同類型的讀/寫操作組合的執(zhí)行順序的放松,可以把常見處理器的內(nèi)存模型劃分為如下幾種類型:

  • 放松程序中寫-讀操作的順序,由此產(chǎn)生了Total Store Ordering內(nèi)存模型(簡(jiǎn)稱為 TSO)。
  • 在上面的基礎(chǔ)上,繼續(xù)放松程序中寫-寫操作的順序,由此產(chǎn)生了Partial Store Order內(nèi)存模型(簡(jiǎn)稱為 PSO)。
  • 在前面兩條的基礎(chǔ)上,繼續(xù)放松程序中讀-寫和讀-讀操作的順序,由此產(chǎn)生了Relaxed Memory Order內(nèi)存模型(簡(jiǎn)稱為 RMO)和 PowerPC 內(nèi)存模型。
處理器內(nèi)存模型的特征表

從上圖中可知:

  • 所有處理器內(nèi)存模型都允許寫-讀重排序,因?yàn)槎际褂昧?寫緩存區(qū)。
    由于寫緩存區(qū)僅對(duì)當(dāng)前處理器可見,這個(gè)特性導(dǎo)致當(dāng)前處理器可以比其他處理器先看到臨時(shí)保存在自己寫緩存區(qū)中的寫。
  • 從上到下,模型由強(qiáng)變?nèi)?。越是追求性能的處理器,?nèi)存模型設(shè)計(jì)得會(huì)越弱。

由于常見的處理器內(nèi)存模型比JMM要弱,Java編譯器在生成字節(jié)碼時(shí),會(huì)在執(zhí)行指令序列的適當(dāng)位置插入 內(nèi)存屏障 來限制處理器的重排序。

JMM插入內(nèi)存屏障的示意圖

各種內(nèi)存模型之間的關(guān)系

JMM 是一個(gè)語言級(jí)的內(nèi)存模型。
處理器內(nèi)存模型 是硬件級(jí)的內(nèi)存模型。
順序一致性內(nèi)存模型 是一個(gè)理論參考模型。

從下圖可以看出:
常見的4處理器內(nèi)存模型 比常用的3語言內(nèi)存模型,
處理器內(nèi)存模型語言內(nèi)存模型 都比 順序一致性內(nèi)存模型
同處理器內(nèi)存模型一樣,越是追求執(zhí)行性能的語言,內(nèi)存模型設(shè)計(jì)得會(huì)越弱。

各種CPU內(nèi)存模型的強(qiáng)弱對(duì)比示意圖

JMM的內(nèi)存可見性保證

按程序類型,Java程序的內(nèi)存可見性保證可以分為下列3類:

  • 單線程程序:不會(huì)出現(xiàn)內(nèi)存可見性問題。JMM為它們提供了最小安全性保障:線程執(zhí)行時(shí)讀取到的值,要么是之前某個(gè)線程寫入的值,要么是默認(rèn)值(0、null、false)。
  • 正確同步的多線程程序:程序的執(zhí)行將具有順序一致性。這是JMM關(guān)注的重點(diǎn),JMM通過限制編譯器和處理器的重排序來為程序員提供內(nèi)存可見性保證。
  • 未同步/未正確同步的多線程程序:JMM為它們提供了最小安全性保障:線程執(zhí)行時(shí)讀取到的值,要么是之前某個(gè)線程寫入的值,要么是默認(rèn)值(0、null、false)。
JSR-133對(duì)舊內(nèi)存模型的修補(bǔ)

JSR-133 對(duì) JDK 5 之前的舊內(nèi)存模型的修補(bǔ)主要有兩個(gè):

  • 增強(qiáng)volatile的內(nèi)存語義:限制volatile變量與普通變量的重排序,使volatile的寫-讀和鎖的釋放-獲取具有相同的內(nèi)存語義。
  • 增強(qiáng)final的內(nèi)存語義:保證final引用不會(huì)從構(gòu)造函數(shù)內(nèi)逸出的情況下,final具有了初始化安全性。

小結(jié)

本文我們介紹了:

  • 線程之后如何通信以及同步?
  • 命令式編程的兩種通信機(jī)制:共享內(nèi)存消息傳遞。
    Java并發(fā)采用的是 共享內(nèi)存,通信時(shí)隱式進(jìn)行的。
  • Java內(nèi)存模型的抽象結(jié)構(gòu)。 存儲(chǔ)在堆內(nèi)存的實(shí)例域、靜態(tài)域數(shù)組元素等才能在線程間共享。
  • 三種類型的重排序。
  • happens-before 規(guī)則
  • volatile的特性、內(nèi)存語義以及實(shí)現(xiàn)。
  • 鎖的內(nèi)存語義
  • final 域的內(nèi)存語義
  • 基于 volatile類初始化 兩種方式來處理 單例模式 雙重檢查鎖定的優(yōu)化,以及出現(xiàn)問題的根源介紹。
  • 對(duì)各內(nèi)存模型的介紹和對(duì)比。

以上

</article>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

  • JMM的介紹 在上一篇文章中總結(jié)了線程的狀態(tài)轉(zhuǎn)換和一些基本操作,對(duì)多線程已經(jīng)有一點(diǎn)基本的認(rèn)識(shí)了,如果多線程編程只有...
    公子只識(shí)黛玉閱讀 649評(píng)論 0 1
  • 基礎(chǔ)知識(shí) 并發(fā)編程引發(fā)的問題 并發(fā)編程需要關(guān)注兩個(gè)問題. 線程之間是如何通信的? 線程之間是如何同步數(shù)據(jù)的? 在現(xiàn)...
    JimmieYang閱讀 1,671評(píng)論 0 2
  • 1. Java內(nèi)存模型基礎(chǔ) 1.1 并發(fā)編程的兩個(gè)關(guān)鍵問題 線程之間如何通信, 通信是指線程之間如何交換信息, 一...
    ygxing閱讀 343評(píng)論 0 0
  • 1.Java內(nèi)存模型的基礎(chǔ) ①并發(fā)編程模型的兩個(gè)關(guān)鍵問題 線程之間如何通信、線程之間如何同步 通信是指線程之間以何...
    加夕閱讀 830評(píng)論 0 1
  • 線程間通信機(jī)制有兩種:共享內(nèi)存、消息傳遞,Java并發(fā)采用的前者(堆內(nèi)存和線程本地內(nèi)存見得數(shù)據(jù)同步); 指令重排序...
    星冉子閱讀 80評(píng)論 0 0

友情鏈接更多精彩內(nèi)容