欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解happens-before和as-if-serial語義

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

概述

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

指令序列的重排序

我們在編寫代碼的時候,通常自上而下編寫,那么希望執(zhí)行的順序,理論上也是逐步串行執(zhí)行,但是為了提高性能,編譯器和處理器常常會對指令做重排序。

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

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


happens-before語義

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

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

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

程序順序規(guī)則: 對于單個線程中的每個操作,前繼操作happens-before于該線程中的任意后續(xù)操作。
監(jiān)視器鎖規(guī)則: 對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
volatile變量規(guī)則: 對一個volatile域的寫,happens-before于任意后續(xù)對這個volatile域的讀。
傳遞性: 如果A happens-before B,且B happens-before C,那么A happens-before C。

注意:

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

happens-before與JMM的關系如圖所示:

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

重排序

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

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

上面情況,只要重排序兩個操作的執(zhí)行順序,程序的執(zhí)行結果就會被改變。而編譯器和處理器可能會對操作做重排序,但是編譯器和處理器在重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關系的兩個操作的執(zhí)行順序。

注意:

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

as-if-serial語義

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

下面還是以書中的實例(計算圓的面積)進行說明:

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

上面3個操作的數據依賴關系如圖所示:

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

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

as-if-serial語義把單線程程序保護了起來,遵守as-if-serial語義的編譯器、runtime和處理器共同為編寫單線程程序的程序員創(chuàng)建了一個幻覺:單線程程序是按程序的順序來執(zhí)行的。

程序順序規(guī)則

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

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

而這里的第3個happens-before關系,是根據happens-before的傳遞性推導出來的。

注意:

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

重排序對多線程的影響

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

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變量是個標記,用來標識變量a是否已被寫入。這里假設有兩個線程A和B,A首先執(zhí)行writer()方法,隨后B線程接著執(zhí)行reader()方法。線程B在執(zhí)行操作4時,能否看到線程A在操作1對共享變量a的寫入呢?

答案是:不一定能看到。

由于操作1和操作2沒有數據依賴關系,編譯器和處理器可以對這兩個操作重排序;同樣,操作3和操作4沒有數據依賴關系,編譯器和處理器也可以對這兩個操作重排序。

當操作1和操作2重排序時,可能會產生什么效果?(虛箭線標識錯誤的讀操作,用實箭線標識正確的讀操作。)

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

當操作3和操作4重排序時會產生什么效果。下面是操作3和操作4重排序后,程序執(zhí)行的時序圖:

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

注意:

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

參考

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • JDBC簡介_動力節(jié)點Java學院整理

    JDBC簡介_動力節(jié)點Java學院整理

    什么是JDBC?這篇文章就為大家詳細介紹了Java語言中用來規(guī)范客戶端程序如何來訪問數據庫的應用程序接口,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • Java 如何在switch case語句中聲明變量

    Java 如何在switch case語句中聲明變量

    這篇文章主要介紹了Java 如何在switch case語句中聲明變量,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 解決idea 通過build project 手動觸發(fā)熱部署失敗的問題

    解決idea 通過build project 手動觸發(fā)熱部署失敗的問題

    在debug運行項目的過程中,并且保證(不添加方法,不修改方法名)一定的規(guī)則的情況下,可以通過build project 來手動熱部署項目,本文給大家介紹解決idea 通過build project 手動觸發(fā)熱部署失敗的問題,感興趣的朋友一起看看吧
    2023-12-12
  • Java靜態(tài)代理與動態(tài)代理案例詳解

    Java靜態(tài)代理與動態(tài)代理案例詳解

    這篇文章主要介紹了Java靜態(tài)代理與動態(tài)代理案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • 如何使用Playwright對Java API實現自動視覺測試

    如何使用Playwright對Java API實現自動視覺測試

    這篇文章主要介紹了如何使用Playwright對Java API實現自動視覺測試,幫助大家更好的理解和使用Playwright,感興趣的朋友可以了解下
    2021-01-01
  • IDEA集成JProfiler11可視化工具的詳細流程(安裝、集成、測試)

    IDEA集成JProfiler11可視化工具的詳細流程(安裝、集成、測試)

    小編打算在IDEA中集成一下JProfiler11(現在有12版本了)工具,到網上搜都沒有找到合適的,于是自己動手寫個,關于IDEA集成JProfiler11可視化工具(安裝、集成、測試)相關知識感興趣的朋友一起看看吧
    2021-06-06
  • 如何判斷java是32位的還是64位的

    如何判斷java是32位的還是64位的

    這篇文章主要介紹了如何判斷java是32位的還是64位的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Spring各版本新特性的介紹

    Spring各版本新特性的介紹

    今天小編就為大家分享一篇關于Spring各版本新特性的介紹,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 詳解Java實踐之適配器模式

    詳解Java實踐之適配器模式

    在計算機編程中,適配器模式(有時候也稱包裝樣式或者包裝)將一個類的接口適配成用戶所期待的。一個適配允許通常因為接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中
    2021-06-06
  • 如何在32位Windows系統下安裝Java

    如何在32位Windows系統下安裝Java

    這篇文章主要介紹了如何在32位Windows系統下安裝Java,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04

最新評論