最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解happens-before和as-if-serial語(yǔ)義

 更新時(shí)間:2019年05月21日 11:44:10   作者:andy周  
本文大部分整理自《Java并發(fā)編程的藝術(shù)》,溫故而知新,加深對(duì)基礎(chǔ)的理解程度。下面可以和小編來(lái)一起學(xué)習(xí)下

概述

本文大部分整理自《Java并發(fā)編程的藝術(shù)》,溫故而知新,加深對(duì)基礎(chǔ)的理解程度。

指令序列的重排序

我們?cè)诰帉懘a的時(shí)候,通常自上而下編寫,那么希望執(zhí)行的順序,理論上也是逐步串行執(zhí)行,但是為了提高性能,編譯器和處理器常常會(huì)對(duì)指令做重排序。

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

從Java源代碼到最終實(shí)際執(zhí)行的指令序列,會(huì)分別經(jīng)歷下面3種重排序:


happens-before語(yǔ)義

從JDK 5開始,Java使用新的內(nèi)存模型,使用happens-before的概念來(lái)闡述操作之間的內(nèi)存可見性。那到底什么是happens-before呢?

在JMM中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見,那么這兩個(gè)操作之間必須要存在happens-before關(guān)系,這里提到的兩個(gè)操作既可以是在一個(gè)線程之內(nèi),也可以是在不同線程之間。

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

程序順序規(guī)則: 對(duì)于單個(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。

注意:

兩個(gè)操作之間具有happens-before關(guān)系,并不意味著前一個(gè)操作必須要在后一個(gè)操作之前執(zhí)行,happens-before僅僅要求前一個(gè)操作(執(zhí)行的結(jié)果)對(duì)后一個(gè)操作可見,且前一個(gè)操作按順序排在第二個(gè)操作之前。

happens-before與JMM的關(guān)系如圖所示:

如圖所示,一個(gè)happens-before規(guī)則對(duì)應(yīng)于一個(gè)或多個(gè)編譯器和處理器重排序規(guī)則。

重排序

重排序指的是:編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段。

如果兩個(gè)操作訪問(wèn)同一個(gè)變量,且這兩個(gè)操作中有一個(gè)為寫操作,此時(shí)這兩個(gè)操作之間就存在數(shù)據(jù)依賴性。數(shù)據(jù)依賴分為下列3種類型:

上面情況,只要重排序兩個(gè)操作的執(zhí)行順序,程序的執(zhí)行結(jié)果就會(huì)被改變。而編譯器和處理器可能會(huì)對(duì)操作做重排序,但是編譯器和處理器在重排序時(shí),會(huì)遵守?cái)?shù)據(jù)依賴性,編譯器和處理器不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。

注意:

這里所說(shuō)的數(shù)據(jù)依賴性僅針對(duì)單個(gè)處理器中執(zhí)行的指令序列和單個(gè)線程中執(zhí)行的操作,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。

as-if-serial語(yǔ)義

as-if-serial語(yǔ)義的意思是:不管怎么重排序,單線程程序的執(zhí)行結(jié)果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語(yǔ)義。所以編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因?yàn)檫@種重排序會(huì)改變執(zhí)行結(jié)果。但是,如果操作之間不存在數(shù)據(jù)依賴關(guān)系,這些操作就可能被編譯器和處理器重排序。

下面還是以書中的實(shí)例(計(jì)算圓的面積)進(jìn)行說(shuō)明:

double pi = 3.14;  
// Adouble r = 1.0;  
// Bdouble area = pi * r * r; // C

上面3個(gè)操作的數(shù)據(jù)依賴關(guān)系如圖所示:

A和C之間存在數(shù)據(jù)依賴關(guān)系,同時(shí)B和C之間也存在數(shù)據(jù)依賴關(guān)系。因此在最終執(zhí)行的指令序列中,C不能被重排序到A和B的前面(因?yàn)镃排到A和B的前面,程序的結(jié)果將會(huì)被改變)。但A和B之間沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以重排序A和B之間的執(zhí)行順序。

該程序的兩種可能執(zhí)行順序:

as-if-serial語(yǔ)義把單線程程序保護(hù)了起來(lái),遵守as-if-serial語(yǔ)義的編譯器、runtime和處理器共同為編寫單線程程序的程序員創(chuàng)建了一個(gè)幻覺:?jiǎn)尉€程程序是按程序的順序來(lái)執(zhí)行的。

程序順序規(guī)則

根據(jù)happens-before的程序順序規(guī)則,上面計(jì)算圓的面積的示例代碼存在3個(gè)happens-before關(guān)系。

1) A happens-before B。
2) B happens-before C。
3) A happens-before C。

而這里的第3個(gè)happens-before關(guān)系,是根據(jù)happens-before的傳遞性推導(dǎo)出來(lái)的。

注意:

這里A happens-before B,但實(shí)際執(zhí)行時(shí)B卻可以排在A之前執(zhí)行,JMM并不要求A一定要在B之前執(zhí)行。JMM僅僅要求前一個(gè)操作(執(zhí)行的結(jié)果)對(duì)后一個(gè)操作可見,且前一個(gè)操作按順序排在第二個(gè)操作之前。這里操作A的執(zhí)行結(jié)果不需要對(duì)操作B可見,而且重排序操作A和操作B后的執(zhí)行結(jié)果,與操作A和操作B按happens-before順序執(zhí)行的結(jié)果一致。在這種情況下,JMM會(huì)認(rèn)為這種重排序并不非法,JMM允許這種重排序。

重排序?qū)Χ嗑€程的影響

重排序是否會(huì)改變多線程程序的執(zhí)行結(jié)果?還是借用書中的一個(gè)例子:

class ReorderExample {
 int a = 0;
 boolean flag = false;
 public void writer() {
  a = 1;   // 1
  flag = true;  // 2
 }
 public void reader() {
  if (flag) {  // 3
  int i = a * a; // 4
  }
 }
}

flag變量是個(gè)標(biāo)記,用來(lái)標(biāo)識(shí)變量a是否已被寫入。這里假設(shè)有兩個(gè)線程A和B,A首先執(zhí)行writer()方法,隨后B線程接著執(zhí)行reader()方法。線程B在執(zhí)行操作4時(shí),能否看到線程A在操作1對(duì)共享變量a的寫入呢?

答案是:不一定能看到。

由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以對(duì)這兩個(gè)操作重排序;同樣,操作3和操作4沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器也可以對(duì)這兩個(gè)操作重排序。

當(dāng)操作1和操作2重排序時(shí),可能會(huì)產(chǎn)生什么效果?(虛箭線標(biāo)識(shí)錯(cuò)誤的讀操作,用實(shí)箭線標(biāo)識(shí)正確的讀操作。)

如圖所示,操作1和操作2做了重排序。程序執(zhí)行時(shí),線程A首先寫標(biāo)記變量flag,隨后線程B讀這個(gè)變量。由于條件判斷為真,線程B將讀取變量a。此時(shí),變量a還沒有被線程A寫入,在這里多線程程序的語(yǔ)義被重排序破壞了!

當(dāng)操作3和操作4重排序時(shí)會(huì)產(chǎn)生什么效果。下面是操作3和操作4重排序后,程序執(zhí)行的時(shí)序圖:

在程序中,操作3和操作4存在控制依賴關(guān)系。當(dāng)代碼中存在控制依賴性時(shí),會(huì)影響指令序列執(zhí)行的并行度。為此,編譯器和處理器會(huì)采用猜測(cè)執(zhí)行來(lái)克服控制相關(guān)性對(duì)并行度的影響。以處理器的猜測(cè)執(zhí)行為例,執(zhí)行線程B的處理器可以提前讀取并計(jì)算a*a,然后把計(jì)算結(jié)果臨時(shí)保存到一個(gè)名為重排序緩沖的硬件緩存中。當(dāng)操作3的條件判斷為真時(shí),就把該計(jì)算結(jié)果寫入變量i中。猜測(cè)執(zhí)行實(shí)質(zhì)上對(duì)操作3和4做了重排序,在這里重排序破壞了多線程程序的語(yǔ)義!

注意:

在單線程程序中,對(duì)存在控制依賴的操作重排序,不會(huì)改變執(zhí)行結(jié)果。
在多線程程序中,對(duì)存在控制依賴的操作重排序,可能會(huì)改變程序的執(zhí)行結(jié)果。

參考

《Java并發(fā)編程的藝術(shù)》

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java8日期時(shí)間類LocalDateTime比較大小舉例

    Java8日期時(shí)間類LocalDateTime比較大小舉例

    LocalDate是Java?8中的日期類之一,它表示一個(gè)日期,下面這篇文章主要給大家介紹了關(guān)于Java8日期時(shí)間類LocalDateTime比較大小的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-05-05
  • Java調(diào)用Python代碼的幾種方法小結(jié)

    Java調(diào)用Python代碼的幾種方法小結(jié)

    Python語(yǔ)言有豐富的系統(tǒng)管理、數(shù)據(jù)處理、統(tǒng)計(jì)類軟件包,因此從java應(yīng)用中調(diào)用Python代碼的需求很常見、實(shí)用,本文介紹幾種方法從java調(diào)用Python代碼,從而最大化利用兩個(gè)語(yǔ)言的優(yōu)勢(shì),需要的朋友可以參考下
    2025-01-01
  • 解決mybatis映射結(jié)果集失效的問(wèn)題

    解決mybatis映射結(jié)果集失效的問(wèn)題

    這篇文章主要介紹了解決mybatis映射結(jié)果集失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • java貪吃蛇游戲?qū)崿F(xiàn)代碼

    java貪吃蛇游戲?qū)崿F(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了java貪吃蛇游戲?qū)崿F(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • Java 數(shù)組轉(zhuǎn)List的四種方式小結(jié)

    Java 數(shù)組轉(zhuǎn)List的四種方式小結(jié)

    本文主要介紹了四種將Java數(shù)組轉(zhuǎn)換為L(zhǎng)ist的方法,包括使用Arrays.asList、ArrayList構(gòu)造器、Collections.addAll以及JDK8的Stream,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • java的Arrays工具類實(shí)戰(zhàn)

    java的Arrays工具類實(shí)戰(zhàn)

    java.util.Arrays類能方便地操作數(shù)組,它提供的所有方法都是靜態(tài)的。Arrays作為一個(gè)工具類,能很好的操作數(shù)組。下面介紹主要使用的幾個(gè)函數(shù)
    2016-12-12
  • jmeter添加自定函數(shù)的實(shí)例(jmeter5.3+IntelliJ IDEA)

    jmeter添加自定函數(shù)的實(shí)例(jmeter5.3+IntelliJ IDEA)

    這篇文章主要介紹了jmeter添加自定函數(shù)的實(shí)例(jmeter5.3+IntelliJ IDEA),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java String字符串的常用使用方法

    Java String字符串的常用使用方法

    String是JDK提供的一個(gè)類,是引用類型,并不是基本的數(shù)據(jù)類型,String用于字符串操作,在之前學(xué)習(xí)c語(yǔ)言的時(shí)候,對(duì)于一些字符串,會(huì)初始化字符數(shù)組表示,本文給大家介紹Java String字符串的常用使用方法,感興趣的朋友一起看看吧
    2025-04-04
  • 使用sharding-jdbc實(shí)現(xiàn)水平分表的示例代碼

    使用sharding-jdbc實(shí)現(xiàn)水平分表的示例代碼

    本文主要介紹了sharding-jdbc實(shí)現(xiàn)水平分表,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • javaCV開發(fā)詳解之推流器和錄制器的實(shí)現(xiàn)

    javaCV開發(fā)詳解之推流器和錄制器的實(shí)現(xiàn)

    這篇文章主要介紹了javaCV開發(fā)詳解之推流器和錄制器實(shí)現(xiàn),對(duì)JavaCV感興趣的同學(xué),可以參考下
    2021-04-04

最新評(píng)論

那坡县| 民勤县| 闽清县| 托克逊县| 建昌县| 洛宁县| 十堰市| 精河县| 石首市| 鸡东县| 阿勒泰市| 唐山市| 鲁甸县| 将乐县| 余干县| 武安市| 武汉市| 故城县| 益阳市| 菏泽市| 阿城市| 乐都县| 孟州市| 杭锦旗| 兖州市| 禹城市| 井陉县| 涞水县| 甘孜县| 巧家县| 化州市| 友谊县| 乐东| 河池市| 海口市| 邛崃市| 田林县| 宁明县| 郁南县| 明水县| 新平|