亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

深入理解Java內(nèi)存模型(三)——順序一致性

aristark / 3645人閱讀

摘要:下面是該程序在兩個內(nèi)存模型中的執(zhí)行時序?qū)Ρ葓D在順序一致性模型中,所有操作完全按程序的順序串行執(zhí)行。不保證未同步程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果一致。

前情提要 深入理解Java內(nèi)存模型(二)——重排序

數(shù)據(jù)競爭與順序一致性保證

當(dāng)程序未正確同步時,就會存在數(shù)據(jù)競爭。java內(nèi)存模型規(guī)范對數(shù)據(jù)競爭的定義如下:

在一個線程中寫一個變量,

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

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

當(dāng)代碼中包含數(shù)據(jù)競爭時,程序的執(zhí)行往往產(chǎn)生違反直覺的結(jié)果(前一章的示例正是如此)。如果一個多線程程序能正確同步,這個程序?qū)⑹且粋€沒有數(shù)據(jù)競爭的程序。

JMM對正確同步的多線程程序的內(nèi)存一致性做了如下保證:

如果程序是正確同步的,程序的執(zhí)行將具有順序一致性(sequentially consistent)–即程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同(馬上我們將會看到,這對于程序員來說是一個極強(qiáng)的保證)。這里的同步是指廣義上的同步,包括對常用同步原語(lock,volatile和final)的正確使用。

順序一致性內(nèi)存模型

順序一致性內(nèi)存模型是一個被計算機(jī)科學(xué)家理想化了的理論參考模型,它為程序員提供了極強(qiáng)的內(nèi)存可見性保證。順序一致性內(nèi)存模型有兩大特性:

一個線程中的所有操作必須按照程序的順序來執(zhí)行。

(不管程序是否同步)所有線程都只能看到一個單一的操作執(zhí)行順序。在順序一致性內(nèi)存模型中,每個操作都必須原子執(zhí)行且立刻對所有線程可見。

順序一致性內(nèi)存模型為程序員提供的視圖如下:

在概念上,順序一致性模型有一個單一的全局內(nèi)存,這個內(nèi)存通過一個左右擺動的開關(guān)可以連接到任意一個線程。同時,每一個線程必須按程序的順序來執(zhí)行內(nèi)存讀/寫操作。從上圖我們可以看出,在任意時間點最多只能有一個線程可以連接到內(nèi)存。當(dāng)多個線程并發(fā)執(zhí)行時,圖中的開關(guān)裝置能把所有線程的所有內(nèi)存讀/寫操作串行化。

為了更好的理解,下面我們通過兩個示意圖來對順序一致性模型的特性做進(jìn)一步的說明。

假設(shè)有兩個線程A和B并發(fā)執(zhí)行。其中A線程有三個操作,它們在程序中的順序是:A1->A2->A3。B線程也有三個操作,它們在程序中的順序是:B1->B2->B3

假設(shè)這兩個線程使用監(jiān)視器來正確同步:A線程的三個操作執(zhí)行后釋放監(jiān)視器,隨后B線程獲取同一個監(jiān)視器。那么程序在順序一致性模型中的執(zhí)行效果將如下圖所示:

現(xiàn)在我們再假設(shè)這兩個線程沒有做同步,下面是這個未同步程序在順序一致性模型中的執(zhí)行示意圖:

未同步程序在順序一致性模型中雖然整體執(zhí)行順序是無序的,但所有線程都只能看到一個一致的整體執(zhí)行順序。以上圖為例,線程A和B看到的執(zhí)行順序都是:B1->A1->A2->B2->A3->B3。之所以能得到這個保證是因為順序一致性內(nèi)存模型中的每個操作必須立即對任意線程可見。

但是,在JMM中就沒有這個保證。未同步程序在JMM中不但整體的執(zhí)行順序是無序的,而且所有線程看到的操作執(zhí)行順序也可能不一致。比如,在當(dāng)前線程把寫過的數(shù)據(jù)緩存在本地內(nèi)存中,且還沒有刷新到主內(nèi)存之前,這個寫操作僅對當(dāng)前線程可見;從其他線程的角度來觀察,會認(rèn)為這個寫操作根本還沒有被當(dāng)前線程執(zhí)行。只有當(dāng)前線程把本地內(nèi)存中寫過的數(shù)據(jù)刷新到主內(nèi)存之后,這個寫操作才能對其他線程可見。在這種情況下,當(dāng)前線程和其它線程看到的操作執(zhí)行順序?qū)⒉灰恢隆?/p> 同步程序的順序一致性效果

下面我們對前面的示例程序ReorderExample用監(jiān)視器來同步,看看正確同步的程序如何具有順序一致性。

請看下面的示例代碼:

class SynchronizedExample {
int a = 0;
boolean flag = false;

public synchronized void writer() {
    a = 1;
    flag = true;
}

public synchronized void reader() {
    if (flag) {
        int i = a;
        ……
    }
}
}

上面示例代碼中,假設(shè)A線程執(zhí)行writer()方法后,B線程執(zhí)行reader()方法。這是一個正確同步的多線程程序。根據(jù)JMM規(guī)范,該程序的執(zhí)行結(jié)果將與該程序在順序一致性模型中的執(zhí)行結(jié)果相同。下面是該程序在兩個內(nèi)存模型中的執(zhí)行時序?qū)Ρ葓D:

在順序一致性模型中,所有操作完全按程序的順序串行執(zhí)行。而在JMM中,臨界區(qū)內(nèi)的代碼可以重排序(但JMM不允許臨界區(qū)內(nèi)的代碼“逸出”到臨界區(qū)之外,那樣會破壞監(jiān)視器的語義)。JMM會在退出監(jiān)視器和進(jìn)入監(jiān)視器這兩個關(guān)鍵時間點做一些特別處理,使得線程在這兩個時間點具有與順序一致性模型相同的內(nèi)存視圖(具體細(xì)節(jié)后文會說明)。雖然線程A在臨界區(qū)內(nèi)做了重排序,但由于監(jiān)視器的互斥執(zhí)行的特性,這里的線程B根本無法“觀察”到線程A在臨界區(qū)內(nèi)的重排序。這種重排序既提高了執(zhí)行效率,又沒有改變程序的執(zhí)行結(jié)果。

從這里我們可以看到JMM在具體實現(xiàn)上的基本方針:在不改變(正確同步的)程序執(zhí)行結(jié)果的前提下,盡可能的為編譯器和處理器的優(yōu)化打開方便之門。

未同步程序的執(zhí)行特性

對于未同步或未正確同步的多線程程序,JMM只提供最小安全性:線程執(zhí)行時讀取到的值,要么是之前某個線程寫入的值,要么是默認(rèn)值(0,null,false),JMM保證線程讀操作讀取到的值不會無中生有(out of thin air)的冒出來。為了實現(xiàn)最小安全性,JVM在堆上分配對象時,首先會清零內(nèi)存空間,然后才會在上面分配對象(JVM內(nèi)部會同步這兩個操作)。因此,在以清零的內(nèi)存空間(pre-zeroed memory)分配對象時,域的默認(rèn)初始化已經(jīng)完成了。

JMM不保證未同步程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果一致。因為未同步程序在順序一致性模型中執(zhí)行時,整體上是無序的,其執(zhí)行結(jié)果無法預(yù)知。保證未同步程序在兩個模型中的執(zhí)行結(jié)果一致毫無意義。

和順序一致性模型一樣,未同步程序在JMM中的執(zhí)行時,整體上也是無序的,其執(zhí)行結(jié)果也無法預(yù)知。同時,未同步程序在這兩個模型中的執(zhí)行特性有下面幾個差異:

順序一致性模型保證單線程內(nèi)的操作會按程序的順序執(zhí)行,而JMM不保證單線程內(nèi)的操作會按程序的順序執(zhí)行(比如上面正確同步的多線程程序在臨界區(qū)內(nèi)的重排序)。這一點前面已經(jīng)講過了,這里就不再贅述。

順序一致性模型保證所有線程只能看到一致的操作執(zhí)行順序,而JMM不保證所有線程能看到一致的操作執(zhí)行順序。這一點前面也已經(jīng)講過,這里就不再贅述。

JMM不保證對64位的long型和double型變量的讀/寫操作具有原子性,而順序一致性模型保證對所有的內(nèi)存讀/寫操作都具有原子性。

第3個差異與處理器總線的工作機(jī)制密切相關(guān)。在計算機(jī)中,數(shù)據(jù)通過總線在處理器和內(nèi)存之間傳遞。每次處理器和內(nèi)存之間的數(shù)據(jù)傳遞都是通過一系列步驟來完成的,這一系列步驟稱之為總線事務(wù)(bus transaction)??偩€事務(wù)包括讀事務(wù)(read transaction)和寫事務(wù)(write transaction)。讀事務(wù)從內(nèi)存?zhèn)魉蛿?shù)據(jù)到處理器,寫事務(wù)從處理器傳送數(shù)據(jù)到內(nèi)存,每個事務(wù)會讀/寫內(nèi)存中一個或多個物理上連續(xù)的字。這里的關(guān)鍵是,總線會同步試圖并發(fā)使用總線的事務(wù)。在一個處理器執(zhí)行總線事務(wù)期間,總線會禁止其它所有的處理器和I/O設(shè)備執(zhí)行內(nèi)存的讀/寫。下面讓我們通過一個示意圖來說明總線的工作機(jī)制:

如上圖所示,假設(shè)處理器A,B和C同時向總線發(fā)起總線事務(wù),這時總線仲裁(bus arbitration)會對競爭作出裁決,這里我們假設(shè)總線在仲裁后判定處理器A在競爭中獲勝(總線仲裁會確保所有處理器都能公平的訪問內(nèi)存)。此時處理器A繼續(xù)它的總線事務(wù),而其它兩個處理器則要等待處理器A的總線事務(wù)完成后才能開始再次執(zhí)行內(nèi)存訪問。假設(shè)在處理器A執(zhí)行總線事務(wù)期間(不管這個總線事務(wù)是讀事務(wù)還是寫事務(wù)),處理器D向總線發(fā)起了總線事務(wù),此時處理器D的這個請求會被總線禁止。

總線的這些工作機(jī)制可以把所有處理器對內(nèi)存的訪問以串行化的方式來執(zhí)行;在任意時間點,最多只能有一個處理器能訪問內(nèi)存。這個特性確保了單個總線事務(wù)之中的內(nèi)存讀/寫操作具有原子性。

在一些32位的處理器上,如果要求對64位數(shù)據(jù)的寫操作具有原子性,會有比較大的開銷。為了照顧這種處理器,java語言規(guī)范鼓勵但不強(qiáng)求JVM對64位的long型變量和double型變量的寫具有原子性。當(dāng)JVM在這種處理器上運行時,會把一個64位long/ double型變量的寫操作拆分為兩個32位的寫操作來執(zhí)行。這兩個32位的寫操作可能會被分配到不同的總線事務(wù)中執(zhí)行,此時對這個64位變量的寫將不具有原子性。

當(dāng)單個內(nèi)存操作不具有原子性,將可能會產(chǎn)生意想不到后果。請看下面示意圖:

如上圖所示,假設(shè)處理器A寫一個long型變量,同時處理器B要讀這個long型變量。處理器A中64位的寫操作被拆分為兩個32位的寫操作,且這兩個32位的寫操作被分配到不同的寫事務(wù)中執(zhí)行。同時處理器B中64位的讀操作被分配到單個的讀事務(wù)中執(zhí)行。當(dāng)處理器A和B按上圖的時序來執(zhí)行時,處理器B將看到僅僅被處理器A“寫了一半“的無效值。

注意,在JSR -133之前的舊內(nèi)存模型中,一個64位long/ double型變量的讀/寫操作可以被拆分為兩個32位的讀/寫操作來執(zhí)行。從JSR -133內(nèi)存模型開始(即從JDK5開始),僅僅只允許把一個64位long/ double型變量的寫操作拆分為兩個32位的寫操作來執(zhí)行,任意的讀操作在JSR -133中都必須具有原子性(即任意讀操作必須要在單個讀事務(wù)中執(zhí)行)。

參考文獻(xiàn)

JSR-133: Java Memory Model and Thread Specification

Shared memory consistency models: A tutorial

The JSR-133 Cookbook for Compiler Writers

深入理解計算機(jī)系統(tǒng)(原書第2版)

UNIX Systems for Modern Architectures: Symmetric Multiprocessing and Caching for Kernel Programmers

作者簡介

程曉明,Java軟件工程師,國家認(rèn)證的系統(tǒng)分析師、信息項目管理師。專注于并發(fā)編程。個人郵箱:asst2003@163.com。

請看下篇 深入理解Java內(nèi)存模型(四)—— volatile

via ifeve

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/66143.html

相關(guān)文章

  • 深入理解Java內(nèi)存模型(二)——重排序

    摘要:前情提要深入理解內(nèi)存模型一基礎(chǔ)編譯器運行時會對指令進(jìn)行重排序。以處理器的猜測執(zhí)行為例,執(zhí)行線程的處理器可以提前讀取并計算,然后把計算結(jié)果臨時保存到一個名為重排序緩沖的硬件緩存中。請看下篇深入理解內(nèi)存模型三順序一致性 前情提要 深入理解Java內(nèi)存模型(一)——基礎(chǔ) Java編譯器、運行時會對指令進(jìn)行重排序。這種重排序在單線程和多線程情況下分別有什么影響呢? 數(shù)據(jù)依賴性 如果兩個操...

    tunny 評論0 收藏0
  • 深入理解 Java 內(nèi)存模型》讀書筆記

    摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過一遍,現(xiàn)在學(xué)的東西越多,感覺那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6RtPu3BNx3zps1JhSmPICRw7QgeOmxOfTb...

    姘存按 評論0 收藏0
  • 深入理解 Java 內(nèi)存模型》讀書筆記

    摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過一遍,現(xiàn)在學(xué)的東西越多,感覺那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://segmentfault.com/img/remote/1460000013474312?w=1920&h=1271); 前提 《深...

    xuexiangjys 評論0 收藏0
  • 深入理解Java內(nèi)存模型(七)——總結(jié)

    摘要:編譯器,和處理器會共同確保單線程程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果相同。正確同步的多線程程序的執(zhí)行將具有順序一致性程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同。 前情提要 深入理解Java內(nèi)存模型(六)——final 處理器內(nèi)存模型 順序一致性內(nèi)存模型是一個理論參考模型,JMM和處理器內(nèi)存模型在設(shè)計時通常會把順序一致性內(nèi)存模型作為參照。JMM和處理器內(nèi)...

    paney129 評論0 收藏0
  • 深入理解Java內(nèi)存模型(一)——基礎(chǔ)

    摘要:線程之間的通信由內(nèi)存模型本文簡稱為控制,決定一個線程對共享變量的寫入何時對另一個線程可見。為了保證內(nèi)存可見性,編譯器在生成指令序列的適當(dāng)位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序。 并發(fā)編程模型的分類 在并發(fā)編程中,我們需要處理兩個關(guān)鍵問題:線程之間如何通信及線程之間如何同步(這里的線程是指并發(fā)執(zhí)行的活動實體)。通信是指線程之間以何種機(jī)制來交換信息。在命令式編程中,線程之間的...

    jsdt 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<