激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - Java中鎖的實現和內存語義淺析

Java中鎖的實現和內存語義淺析

2021-06-15 09:46蝸牛大師 Java教程

這篇文章主要給大家介紹了關于Java中鎖的實現和內存語義的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1. 概述

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

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

2. 鎖的內存語義

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

鎖的內存語義與volatile的內存語義

  • 鎖獲取與volatile讀有相同的內存語義.
  • 鎖釋放與volatile寫有相同的內存語義.

內存語義總結

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

3. 鎖內存語義的實現

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

而加鎖的關鍵代碼就是

?
1
2
3
protected final boolean compareandsetstate(int expect, int update) {
 return unsafe.compareandswapint(this, stateoffset, expect, update);
}

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

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

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

下面我們來分析在常見的intel x86處理器中, cas是如何同時具有volatile讀和volatile寫的內存語義的.

下面是sun.misc.unsafe類的compareandswapint()方法的源代碼.

?
1
public final native boolean compareandswapint(object var1, long var2, int var4, int var5);

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

?
1
2
3
4
5
6
7
8
9
10
11
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
 }
}

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

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

  • 確保對內存的讀-改-寫操作原子執行. 在pentium及pentium之前的處理器中, 帶有lock前綴的指令在執行期間會鎖住總線, 使得其他處理器暫時無法通過總線訪問內存. 很顯然, 這會帶來昂貴的開銷. 從pentium 4、intel xeon及p6處理器開始, intel使用緩存鎖定(cache locking)
    來保證指令執行的原子性. 緩存鎖定將大大降低lock前綴指令的執行開銷.
  • 禁止該指令, 與之前和之后的讀和寫指令重排序.
  • 把寫緩沖區中的所有數據刷新到內存中.

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

經過上面的分析, 現在我們終于能明白為什么jdk文檔說cas同時具有volatile讀和volatile寫的內存語義了.

從本文對reentrantlock的分析可以看出, 鎖釋放-獲取的內存語義的實現至少有下面兩種方式.

  • 利用volatile變量的寫-讀所具有的內存語義.
  • 利用cas所附帶的volatile讀和volatile寫的內存語義.

4. 總結

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

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

原文鏈接:https://www.cnblogs.com/wuqinglong/p/9962142.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久久久久久亚洲视频 | hdbbwsexvideo| 136福利视频 | 在线观看国产一区二区三区 | 亚洲国产精品久久久久婷婷老年 | 久久国产精品二国产精品中国洋人 | 欧美黑人伦理 | 黄色网电影 | 国产成视频在线观看 | 欧美国产精品久久 | 黄色片在线观看网站 | 国产精品白嫩白嫩大学美女 | 伊人99re | 长泽雅美av| chinese xvideos gay| 成人毛片在线 | 亚洲午夜视频 | 午夜激情视频网站 | 黄片毛片一级 | 精品国产乱码一区二区 | 99精品无人区乱码在线观看 | 精品国产一区二区三区四区在线 | 久久久国产精品电影 | 国外成人在线视频 | 国产理论视频在线观看 | 蜜桃网在线观看 | 精品国产一区二区三区久久久狼牙 | 成人免费毛片一 | 亚洲午夜久久久精品一区二区三区 | 午夜视频免费播放 | 曰韩一级片 | 蜜桃传免费看片www 日本一区二区三区视频在线 | 视频一区二区三区中文字幕 | 国内精品久久久久久久星辰影视 | 黄色网址www | 午夜影院a| 久久中文字幕在线观看 | 羞羞网站在线看 | 爱福利视频| 欧美国产一区二区三区 | 91看片www|