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

并發(fā)編程之Java內(nèi)存模型鎖的內(nèi)存語(yǔ)義

 更新時(shí)間:2021年11月04日 10:03:16   作者:李子捌  
這篇文章主要介紹了并發(fā)編程之Java內(nèi)存模型鎖的內(nèi)存語(yǔ)義,鎖的作用是讓臨界區(qū)互斥執(zhí)行,本文只要圍繞鎖的內(nèi)存語(yǔ)義展開全文內(nèi)容,需要的小伙伴可以參考一下

簡(jiǎn)介:

鎖的作用是讓臨界區(qū)互斥執(zhí)行。本文闡述所得另一個(gè)重要知識(shí)點(diǎn)——鎖的內(nèi)存語(yǔ)義。

1、鎖的釋放-獲取建立的happens-before關(guān)系

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

鎖釋放-獲取的示例代碼:

package com.lizba.p1;

/**
 * <p>
 *      鎖示例代碼
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/10 21:43
 */
public class MonitorExample {

    int a = 0;

    public synchronized void writer() {     // 1;
        a++;                                // 2;
    }                                       // 3;

    public synchronized void reader() {     // 4;
        int i = a;                          // 5;
        System.out.println(i);
    }                                       // 6;
    
}

假設(shè)線程A執(zhí)行writer()方法,隨后線程B執(zhí)行reader()方法。根據(jù)happens-before規(guī)范,這個(gè)過程包含的happens-before關(guān)系可以分為3類。

  • 根據(jù)程序次序規(guī)則:1 happens-before 2,2 happens-before 3, 4 happens-before 5,5 happens-before 6
  • 根據(jù)監(jiān)視器鎖規(guī)則:3 happens-before 4
  • 根據(jù)happens-before的傳遞性,2 happens-before 5

上述happens-before關(guān)系的圖形化表現(xiàn)形式如圖:

總結(jié):

線程A在釋放鎖之前所有可見的共享變量,在線程B獲取同一個(gè)鎖之后,將立即變得對(duì)B線程可見。

2、鎖釋放和獲取的內(nèi)存語(yǔ)義

當(dāng)線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中。以上述MonitorExample程序?yàn)槔?,A線程釋放鎖后共享數(shù)據(jù)的狀態(tài)

共享數(shù)據(jù)的狀態(tài)示意圖如下所示:

當(dāng)線程獲取鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。從而使得被監(jiān)視器鎖保護(hù)的臨界區(qū)代碼必須從主內(nèi)存中讀取共享變量。

鎖獲取的狀態(tài)示意圖:

對(duì)比鎖釋放-獲取鎖的內(nèi)存語(yǔ)義與volatile寫-讀的內(nèi)存語(yǔ)義可以看出:鎖釋放與volatile寫有相同的內(nèi)存語(yǔ)義;鎖獲取與volatile讀有相同的內(nèi)存語(yǔ)義。

總結(jié):

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

3、鎖內(nèi)存的語(yǔ)義實(shí)現(xiàn)

分析ReentrantLock的源代碼,來分析鎖內(nèi)存語(yǔ)義的具體實(shí)現(xiàn)機(jī)制。

示例代碼:

package com.lizba.p1;

import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 *  ReentrantLock示例代碼
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/10 22:17
 */
public class ReentrantLockExample {

    int a = 0;
    ReentrantLock lock = new ReentrantLock();

    public void writer() {
        lock.lock();                 // 獲取鎖
        try {
            a++;
        } finally {
            lock.unlock();          // 釋放鎖
        }
    }

    public void reader() {
        lock.lock();                // 獲取鎖
        try {
            int i = a;
            System.out.println(i);
        } finally {
            lock.unlock();          // 釋放鎖
        }
    }

}

ReentrantLock中,調(diào)用lock()方法獲取鎖;調(diào)用unlock()方法釋放鎖。

ReentrantLock的實(shí)現(xiàn)依賴于Java同步器框架AbstractQueuedSynchronized(AQS) 。AQS使用一個(gè)整型的volatile變量(state)來維護(hù)同步狀態(tài),這個(gè)volatile變量是ReentrantLock內(nèi)存語(yǔ)義實(shí)現(xiàn)的關(guān)鍵。

ReetrantLock的類圖:

ReentrantLock分為公平鎖和非公平鎖,首先分析公平鎖。

使用公平鎖時(shí),加鎖方法lock()的調(diào)用軌跡如下:

  • ReentrantLock: lock(
  • FairSync: lock()
  • AbstractQueuedSynchronizer: acquire(int arg)
  • ReentrantLock: tryAcquire(int acquires)

第4步開始真的加鎖,下面是該方法的源代碼:

 protected final boolean tryAcquire(int acquires) {
     final Thread current = Thread.currentThread();
     // 獲取鎖開始,首先讀取volatile變量state
     int c = getState();
     if (c == 0) {
         if (!hasQueuedPredecessors() &&
             compareAndSetState(0, acquires)) {
             setExclusiveOwnerThread(current);
             return true;
         }
     }
     else if (current == getExclusiveOwnerThread()) {
         int nextc = c + acquires;
         if (nextc < 0)
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

從上面的代碼中可以看出,加鎖方法首先讀取volatile變量state

在使用公平鎖時(shí),解鎖方法unlock()調(diào)用軌跡如下:

  • ReentrantLock: unlock()
  • AbstractQueuedSynchronizer: release(int arg)
  • Sync: tryRelease(int release)

第3步開始真的釋放鎖,下面是該方法的源代碼:

  protected final boolean tryRelease(int releases) {
      int c = getState() - releases;
      if (Thread.currentThread() != getExclusiveOwnerThread())
          throw new IllegalMonitorStateException();
      boolean free = false;
      if (c == 0) {
          free = true;
          setExclusiveOwnerThread(null);
      }
      // 釋放鎖的最后,寫volatile變量state
      setState(c);
      return free;
  }

從上面的代碼中可以看出,釋放鎖的最后寫volatile變量state

總結(jié)公平鎖:

根據(jù)volatile的happens-before規(guī)則,釋放鎖的線程在寫volatile變量之前可見的共享變量,在獲取鎖的線程讀取到同一個(gè)volatile變量后將立即變得對(duì)獲取鎖的線程可見。

現(xiàn)在分析非公平鎖:

注意:非公平鎖的釋放和公平鎖的釋放完全一致,都是上面的源代碼。所以下面只分析非公平鎖的獲取過程。

使用非公平鎖,加鎖方法lock()的調(diào)用軌跡如下:

  • ReentrantLock: lock()
  • NonfairSync: lock()
  • AbstractQueuedSynchronizer: compareAndSetState(int expect, int update)

第3步開始真的加鎖,下面是該方法的源代碼:

// 方法1  
final boolean nonfairTryAcquire(int acquires) {
      final Thread current = Thread.currentThread();
      int c = getState();
      if (c == 0) {
          // 此方法中開始加鎖
          if (compareAndSetState(0, acquires)) {
              setExclusiveOwnerThread(current);
              return true;
          }
      }
      else if (current == getExclusiveOwnerThread()) {
          int nextc = c + acquires;
          if (nextc < 0) // overflow
              throw new Error("Maximum lock count exceeded");
          setState(nextc);
          return true;
      }
      return false;
  }

// 方法2
 protected final boolean compareAndSetState(int expect, int update) {
     // See below for intrinsics setup to support this
     // 該方法是native方法,在JVM中實(shí)現(xiàn)
     return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
 }

該方法以原子操作的方式更新state變量,也就是compareAndSet() (CAS)操作。JDK文檔對(duì)該方法說明如下:如果當(dāng)前狀態(tài)值等于預(yù)期值,則以原子方式同步狀態(tài)設(shè)置為給定更新的值。此操作具有volatile讀和寫的內(nèi)存語(yǔ)義。

接下來分別從編譯器和處理器的角度來分析,CAS如何同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義。

編譯器的角度:

前文已經(jīng)講過,編譯器不會(huì)對(duì)volatile讀與volatile讀后面的任意內(nèi)存操作重排序;編譯器不會(huì)對(duì)volatile寫和volatile寫后前面的任意內(nèi)存操作重排序。組合這兩個(gè)條件,意味著同時(shí)實(shí)現(xiàn)volatile讀和volatile寫的內(nèi)存語(yǔ)義,編譯器不能對(duì)CAS與CAS前面和后面任意內(nèi)存操作重排序。

處理器的角度:

(本人不太懂C++)這一塊總結(jié)需要看JVM源碼,可能會(huì)總結(jié)錯(cuò)誤,如需要深入理解這一塊請(qǐng)查看《Java并發(fā)編程藝術(shù)》53頁(yè)。

sun.misc.Unsafe中的compareAndSwapInt源碼如下:(不懂Unsafe請(qǐng)看往期文章)

 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

這是一個(gè)本地方法。這個(gè)本地方法會(huì)在openJDK中調(diào)用C++代碼,假設(shè)當(dāng)前是X86處理器,程序會(huì)根據(jù)當(dāng)前處理器的類型來決定是非cmpxchg指令添加lock前綴。

  • 程序運(yùn)行在多處理器上,就為cmpxchg指令加上lock前綴(Lock Cmpxchg
  • 程序運(yùn)行在單處理器上,就省略lock前綴(單處理器自身會(huì)維護(hù)單處理器內(nèi)的順序一致性,不需要lock前綴提供的內(nèi)存屏障效果)

intel手冊(cè)對(duì)lock前綴的說明:

  • 對(duì)內(nèi)存的讀-改-寫操作原子執(zhí)行。(總線鎖定/緩存鎖定)
  • 禁止該指令,與之前的讀和寫指令重排序
  • 把寫緩沖區(qū)的所有數(shù)據(jù)刷新到內(nèi)存中

上面的2、3兩點(diǎn)所具有的內(nèi)存屏障的效果,足以同時(shí)實(shí)現(xiàn)volatile讀和volatile寫的內(nèi)存語(yǔ)義。所以JDK文檔說CAS 具有volatile讀和volatile寫的內(nèi)存語(yǔ)義對(duì)于處理器也是符合的。

公平鎖和非公平鎖的總結(jié):

  • 公平鎖和非公平鎖的釋放,最后都需要寫一個(gè)volatile變量state
  • 公平鎖獲取時(shí),首先會(huì)去讀volatile變量
  • 非公平鎖獲取鎖時(shí),首先會(huì)用CAS更新volatile變量,這個(gè)操作同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義

釋放鎖-獲取鎖的內(nèi)存語(yǔ)義的實(shí)現(xiàn)方式總結(jié) :

  • 利用volatile變量的寫-讀所具有的內(nèi)存語(yǔ)義
  • 利用CAS所附帶的volatile讀和volatile寫的內(nèi)存語(yǔ)義

4、concurrent包的實(shí)現(xiàn)

由于Java的CAS同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義,因此Java線程之間的通信方式有以下4種方式

  • A線程寫volatile變量,隨后B線程讀這個(gè)volatile變量
  • A線程寫volatile變量,隨后B線程用CAS更新這個(gè)volatile變量
  • A線程利用CAS更新一個(gè)volatile變量,隨后B線程用CAS更新這個(gè)volatile變量
  • A線程利用CAS更新一個(gè)volatile變量,隨后B線程讀這個(gè)volatile變量

Java的CAS會(huì)使用現(xiàn)代處理器上提供的高效機(jī)器級(jí)別的原子指令,這些原子指令以原子方式對(duì)內(nèi)存執(zhí)行讀-改-寫操作,這是在多處理器實(shí)現(xiàn)同步的關(guān)鍵。同時(shí)volatile變量的讀/寫和CAS可以實(shí)現(xiàn)線程之間的通信。這些特性就是Java整個(gè)concurrent包的基石。

concurrent包的通用化實(shí)現(xiàn)模式:

  • 聲明共享變量volatile
  • 使用CAS的原子條件更新來實(shí)現(xiàn)線程之間的同步
  • 配合volatile的讀/寫和CAS具有的volatile讀和寫的內(nèi)存語(yǔ)義來實(shí)現(xiàn)線程之間的通信。

AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)、非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(java.util.concurrent.atomic包中的類),這些concurrent包中基礎(chǔ)類都是使用這個(gè)模式來實(shí)現(xiàn)的,而concurrent包中的高層類又是依賴于這些基礎(chǔ)類。

圖示concurrent包的實(shí)現(xiàn)示意圖:

到此這篇關(guān)于并發(fā)編程之Java內(nèi)存模型鎖的內(nèi)存語(yǔ)義的文章就介紹到這了,更多相關(guān)Java內(nèi)存模型鎖的內(nèi)存語(yǔ)義內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入了解SpringBoot中的統(tǒng)一返回和統(tǒng)一異常處理

    深入了解SpringBoot中的統(tǒng)一返回和統(tǒng)一異常處理

    這篇文章主要為大家詳細(xì)介紹了SpringBoot項(xiàng)目中常用的統(tǒng)一返回結(jié)果和統(tǒng)一異常處理,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2024-01-01
  • JDK  keytool證書工具功能代碼解析

    JDK keytool證書工具功能代碼解析

    這篇文章主要介紹了JDK keytool證書工具功能代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 因不會(huì)遠(yuǎn)程debug調(diào)試我被項(xiàng)目經(jīng)理嘲笑了

    因不會(huì)遠(yuǎn)程debug調(diào)試我被項(xiàng)目經(jīng)理嘲笑了

    這篇文章主要介紹了遠(yuǎn)程debug調(diào)試的相關(guān)內(nèi)容,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • 一文理解kafka?rebalance負(fù)載均衡

    一文理解kafka?rebalance負(fù)載均衡

    這篇文章主要為大家介紹了kafka?rebalance負(fù)載均衡的深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼

    Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼

    本文主要介紹了Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Java實(shí)現(xiàn)帶附件的郵件發(fā)送功能

    Java實(shí)現(xiàn)帶附件的郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)帶附件的郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 舉例解析Java的設(shè)計(jì)模式編程中里氏替換原則的意義

    舉例解析Java的設(shè)計(jì)模式編程中里氏替換原則的意義

    這篇文章主要介紹了Java的設(shè)計(jì)模式中里氏替換原則的意義,文中舉例來說明里氏替換原則中強(qiáng)調(diào)的繼承特性方面可能帶來的問題,需要的朋友可以參考下
    2016-02-02
  • android中GridView的用法示例

    android中GridView的用法示例

    這篇文章主要介紹了android中GridView的用法,對(duì)于Android初學(xué)者很有參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2014-08-08
  • 關(guān)于maven下載慢的問題

    關(guān)于maven下載慢的問題

    這篇文章主要介紹了關(guān)于maven下載慢的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • idea解決springboot中的依賴版本沖突問題

    idea解決springboot中的依賴版本沖突問題

    這篇文章主要介紹了idea解決springboot中的依賴版本沖突問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評(píng)論

镇雄县| 剑阁县| 温泉县| 祁阳县| 长葛市| 永济市| 佛冈县| 柞水县| 巴里| 天峻县| 江西省| 获嘉县| 屏东县| 呈贡县| 绵竹市| 老河口市| 黄浦区| 西青区| 金平| 金溪县| 邢台县| 通城县| 丽江市| 南靖县| 静海县| 宁化县| 定结县| 万宁市| 南汇区| 海南省| 莎车县| 清徐县| 鄱阳县| 梓潼县| 康平县| 赣州市| 蓬莱市| 建阳市| 商水县| 远安县| 墨玉县|