亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中鎖的實現(xiàn)和內(nèi)存語義淺析

 更新時間:2018年11月15日 15:03:01   作者:蝸牛大師  
這篇文章主要給大家介紹了關于Java中鎖的實現(xiàn)和內(nèi)存語義的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1. 概述

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

鎖在實際使用時只是明白鎖限制了并發(fā)訪問, 但是鎖是如何實現(xiàn)并發(fā)訪問的, 同學們可能不太清楚, 下面這篇文章就來揭開鎖的神秘面紗.

2. 鎖的內(nèi)存語義

  • 當線程獲取鎖時, JMM會把線程對應的本地內(nèi)存置為無效. 從而使得被監(jiān)視器保護的臨界區(qū)的變量必須從主內(nèi)存中讀取.
  • 當線程釋放鎖時, JMM會把該線程對應的本地內(nèi)存中的共享變量刷新到主內(nèi)存中(并不是不釋放鎖就不刷新到主內(nèi)存, 只是釋放鎖時把未刷新到主內(nèi)存中的數(shù)據(jù)刷新到主內(nèi)存).

鎖的內(nèi)存語義與volatile的內(nèi)存語義

  • 鎖獲取與volatile讀有相同的內(nèi)存語義.
  • 鎖釋放與volatile寫有相同的內(nèi)存語義.

內(nèi)存語義總結

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

3. 鎖內(nèi)存語義的實現(xiàn)

下面以ReentrantLock為例, 獲取到鎖就是把state改為1(不考慮重入), 釋放鎖時改為0.

而加鎖的關鍵代碼就是

protected final boolean compareAndSetState(int expect, int update) {
 return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

該方法以原子操作的方式更新state變量, 本文把Java的compareAndSet()方法簡稱為CAS. JDK文檔對該方法的說明如下: 如果當前狀態(tài)值等于預期值, 則以原子方式將同步狀態(tài)設置為給定的更新值. 此操作具有volatile讀和寫的內(nèi)存語義.

這里我們分別從編譯器和處理器的角度來分析: CAS如何同時具有volatile讀和volatile寫的內(nèi)存語義.

我們知道, 編譯器不會對volatile讀與volatile讀后面的任意內(nèi)存操作重排序; 編譯器不會對volatile寫與volatile寫前面的任意內(nèi)存操作重排序. 組合這兩個條件, 意味著為了同時實現(xiàn)volatile讀和volatile寫的內(nèi)存語義, 編譯器不能對CAS與CAS前面和后面的任意內(nèi)存操作重排序.

下面我們來分析在常見的intel X86處理器中, CAS是如何同時具有volatile讀和volatile寫的內(nèi)存語義的.

下面是sun.misc.Unsafe類的compareAndSwapInt()方法的源代碼.

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

可以看到, 這是一個本地方法調(diào)用. 這個本地方法在openjdk中依次調(diào)用的c++代碼為: unsafe.cpp, atomic.cpp 和 atomic_windows_x86.inline.hpp. 這個本地方法的最終實現(xiàn)在openjdk的如下位置: openjdk-7-fcs-src-b147-
27_jun_2011\openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp(對應于
Windows操作系統(tǒng), X86處理器). 下面是對應于intel X86處理器的源代碼的片段.

inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
 // alternative for InterlockedCompareExchange
 int mp = os::is_MP();
 __asm {
  mov edx, dest
  mov ecx, exchange_value
  mov eax, compare_value
  LOCK_IF_MP(mp)
  cmpxchg dword ptr [edx], ecx
 }
}

如上面源代碼所示, 程序會根據(jù)當前處理器的類型來決定是否為cmpxchg指令添加lock前綴. 如果程序是在多處理器上運行, 就為cmpxchg指令加上lock前綴(Lock Cmpxchg). 反之, 如果程序是在單處理器上運行, 就省略lock前綴(單處理器自身會維護單處理器內(nèi)的順序一致性, 不需要lock前綴提供的內(nèi)存屏障效果).

intel的手冊對lock前綴的說明如下.

  • 確保對內(nèi)存的讀-改-寫操作原子執(zhí)行. 在Pentium及Pentium之前的處理器中, 帶有l(wèi)ock前綴的指令在執(zhí)行期間會鎖住總線, 使得其他處理器暫時無法通過總線訪問內(nèi)存. 很顯然, 這會帶來昂貴的開銷. 從Pentium 4、Intel Xeon及P6處理器開始, Intel使用緩存鎖定(Cache Locking)
    來保證指令執(zhí)行的原子性. 緩存鎖定將大大降低lock前綴指令的執(zhí)行開銷.
  • 禁止該指令, 與之前和之后的讀和寫指令重排序.
  • 把寫緩沖區(qū)中的所有數(shù)據(jù)刷新到內(nèi)存中.

上面的第2點和第3點所具有的內(nèi)存屏障效果, 足以同時實現(xiàn)volatile讀和volatile寫的內(nèi)存語義.

經(jīng)過上面的分析, 現(xiàn)在我們終于能明白為什么JDK文檔說CAS同時具有volatile讀和volatile寫的內(nèi)存語義了.

從本文對ReentrantLock的分析可以看出, 鎖釋放-獲取的內(nèi)存語義的實現(xiàn)至少有下面兩種方式.

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

4. 總結

對于鎖, 可以這么理解, N個線程去通過CAS去修改一個volatile變量, 但是由于CPU提供的機制, 只能有一個線程修改成功, 修改成功的線程獲得鎖, 其它線程以及后來的線程要么自旋一會兒, 要么直接掛起, 等待獲取鎖的線程釋放鎖時去喚醒. 就是這么個過程.

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關文章

  • SpringBoot使用JDBC獲取相關的數(shù)據(jù)方法

    SpringBoot使用JDBC獲取相關的數(shù)據(jù)方法

    這篇文章主要介紹了SpringBoot使用JDBC獲取相關的數(shù)據(jù)方法,JDBC與數(shù)據(jù)庫建立連接、發(fā)送 操作數(shù)據(jù)庫的語句并處理結果,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • java判斷class子類或父類的實例方法

    java判斷class子類或父類的實例方法

    在本篇文章里小編給大家整理的是關于java判斷class子類或父類的實例方法,需要的朋友們可以參考學習下。
    2020-02-02
  • 詳解如何使用XML配置來定義和管理Spring Bean

    詳解如何使用XML配置來定義和管理Spring Bean

    XML 配置文件是 Spring 中傳統(tǒng)的 Bean 配置方式,通過定義 XML 元素來描述 Bean 及其依賴關系,在 Spring 框架中,Bean 是由 Spring IoC(控制反轉(zhuǎn))容器管理的對象,本文將詳細介紹如何使用 XML 配置來定義和管理 Spring Bean,需要的朋友可以參考下
    2024-06-06
  • Java的Hello World詳解

    Java的Hello World詳解

    當我們學習一門編程語言的時候,我們都會先學如何輸出Hello World!本文通過幾個例子給大家介紹輸出Hello World的代碼,感興趣的朋友一起看看吧
    2021-09-09
  • Java NIO框架Netty簡單使用的示例

    Java NIO框架Netty簡單使用的示例

    本篇文章主要介紹了Java NIO框架Netty簡單使用的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • Java編程學習的幾個典型實例詳解

    Java編程學習的幾個典型實例詳解

    這篇文章主要給大家介紹了Java編程學習的幾個典型實例,其中包括模擬酒店房間管理系統(tǒng)、螺旋矩陣 例或者百雞問題的變形等經(jīng)典實例,具體來一起看詳細內(nèi)容吧,需要的朋友可以參考學習。
    2017-02-02
  • Mybatis常用標簽及屬性小結

    Mybatis常用標簽及屬性小結

    這篇文章主要介紹了Mybatis常用標簽及屬性小結,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-12-12
  • 基于resty?security的Api權限控制與事務支持

    基于resty?security的Api權限控制與事務支持

    這篇文章主要為大家介紹了基于resty?security的Api權限控制與事務支持讓數(shù)據(jù)操作處于事務控制下,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • springboot實現(xiàn)定時任務@Scheduled方式

    springboot實現(xiàn)定時任務@Scheduled方式

    這篇文章主要介紹了springboot實現(xiàn)定時任務@Scheduled方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring注解驅(qū)動之BeanFactoryPostProcessor原理解析

    Spring注解驅(qū)動之BeanFactoryPostProcessor原理解析

    這篇文章主要介紹了Spring注解驅(qū)動之BeanFactoryPostProcessor原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評論