日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发编程—无锁互斥机制及CAS原理

發布時間:2024/4/15 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程—无锁互斥机制及CAS原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

一、CAS簡介

二、AtomicInteger代碼演示

三、CAS 實現

四、弊端


一、CAS簡介

在計算機科學中,比較和交換(Conmpare And Swap)是用于實現多線程同步的原子指令。它將內存位置的內容與給定值進行比較,只有在相同的情況下,將該內存位置的內容修改為新的給定值。這是作為單個原子操作完成的。 原子性保證新值基于最新信息計算; 如果該值在同一時間被另一個線程更新,則寫入將失敗。操作結果必須說明是否進行替換; 這可以通過一個簡單的布爾響應(這個變體通常稱為比較和設置),或通過返回從內存位置讀取的值來完成。

CAS是一種無鎖算法,有3個關鍵操作數?內存中的原數據V,舊的預期值A,需要修改的新值B,當內存值和舊的內存中預期值相等時,將內存中的值更新為新值。

操縱步驟:比較 A 與 V 是否相等。(比較) 如果比較相等,將 B 寫入 V。(交換) 返回操作是否成功。 當多個線程同時對某個資源進行CAS操作,只能有一個線程操作成功,但是并不會阻塞其他線程,其他線程只會收到操作失敗的信號。可見 CAS 其實是一個樂觀鎖

?

如上圖中,主存中保存V值,線程中要使用V值要先從主存中讀取V值到線程的工作內存A中,然后計算后變成B值,最后再把B值寫回到內存V值中。多個線程共用V值都是如此操作。CAS的核心是在將B值寫入到V之前要比較A值和V值是否相同,如果不相同證明此時V值已經被其他線程改變,重新將V值賦給A,并重新計算得到B,如果相同,則將B值賦給V。

如果不使用CAS機制,看看存在什么問題:假如V=1,現在Thread1要對V進行加1,Thread2也要對V進行加1,首先Thread1讀取V=1到自己工作內存A中此時A=1,假設Thread2此時也讀取V=1到自己的工作內存A中,分別進行加1操作后,兩個線程中B的值都為2,此時寫回到V中時發現V的值為2,但是兩個線程分別對V進行加處理結果卻只加了1有問題。

樂觀鎖與悲觀鎖:CAS屬于樂觀鎖,樂觀鎖就是每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。synchronized是悲觀鎖,被一個線程拿到鎖之后,其他線程必須等待該線程釋放鎖,性能較差

二、AtomicInteger代碼演示

在java中,a++不是原子操作,一個簡單的a++操作涉及到三個操作,獲取變量a的內存值,將變量a+1,將新值寫入內存,這里涉及到了兩次內存訪問,如果在多線程環境下,那么會出現并發安全問題。AtomicInteger是一個原子操作類,內部采用的就是CAS無鎖算法。 這里我們分析一下它的內部實現。

AtomicInteger atomicInteger = new AtomicInteger(0); atomicInteger.getAndSet(1);

???? 這里的靜態代碼塊AtomicInteger對象初始化之前就執行,獲取AtomicInteger對象value字段相對AtomicInteger對象的”起始地址”的偏移量,Java對象在內存中存儲的布局可以分為三塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding),”起始地址”的偏移量即是對象頭的偏移量。

static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); } } public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue); }

每次通過內存地址(var2)先從內存中獲取內存中原值(var5),再循環將內存中的原值(var5)與給定內存地址(var2)相比較,如果相等則更新指定預期值(var4),如果不相等則再重試直到成功為止,最后返回舊的內存原值var5。

//var1為AtomicInteger對象,var2為內存地址值,var4為指定的預期值 public final int getAndSetInt(Object var1, long var2, int var4) {int var5;do {//unsafe.getIntVolatile調用本地方法獲取內存中值var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var4));return var5; }

三、CAS 實現

?java.util.concurrent.atomic 包下的原子類 AtomicInteger 中的 compareAndSet 方法最終調用的是 sum.misc.Unsafe 這個類。 看名稱 Unsafe 就是一個不安全的類,這個類是利用了 Java 的類和包在可見性的的規則中的一個恰到好處處的漏洞。Unsafe 這個類為了速度,在Java的安全標準上做出了一定的妥協。再往下尋找我們發現 Unsafe的compareAndSwapInt 是 Native 的方法:

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

也就是說,這幾個 CAS 的方法應該是使用了本地的方法。所以這幾個方法的具體實現需要我們自己去 jdk 的源碼中搜索。 最終到搜索 cmpxchg 函數

inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { // 判斷是否是多核 CPUint mp = os::is_MP();__asm { // 將參數值放入寄存器中mov edx, dest // 注意: dest 是指針類型,這里是把內存地址存入 edx 寄存器中mov ecx, exchange_valuemov eax, compare_value // LOCK_IF_MPcmp mp, 0/** 如果 mp = 0,表明是線程運行在單核 CPU 環境下。此時 je 會跳轉到 L0 標記處,* 也就是越過 _emit 0xF0 指令,直接執行 cmpxchg 指令。也就是不在下面的 cmpxchg 指令* 前加 lock 前綴。*/je L0 /** 0xF0 是 lock 前綴的機器碼,這里沒有使用 lock,而是直接使用了機器碼的形式。至于這樣做的* 原因可以參考知乎的一個回答:* https://www.zhihu.com/question/50878124/answer/123099923*/ _emit 0xF0L0: /** 比較并交換。簡單解釋一下下面這條指令,熟悉匯編的朋友可以略過下面的解釋:* cmpxchg: 即“比較并交換”指令* dword: 全稱是 double word,在 x86/x64 體系中,一個 * word = 2 byte,dword = 4 byte = 32 bit* ptr: 全稱是 pointer,與前面的 dword 連起來使用,表明訪問的內存單元是一個雙字單元* [edx]: [...] 表示一個內存單元,edx 是寄存器,dest 指針值存放在 edx 中。* 那么 [edx] 表示內存地址為 dest 的內存單元* * 這一條指令的意思就是,將 eax 寄存器中的值(compare_value)與 [edx] 雙字內存單元中的值* 進行對比,如果相同,則將 ecx 寄存器中的值(exchange_value)存入 [edx] 內存單元中。*/cmpxchg dword ptr [edx], ecx} }

總結一下 JAVA 的 cas 是怎么實現的:

  • java 的 cas 利用的的是 unsafe 這個類提供的 cas 操作。
  • unsafe 的cas 依賴了的是 jvm 針對不同的操作系統實現的 Atomic::cmpxchg
  • Atomic::cmpxchg 的實現使用了匯編的 cas 操作,并使用 cpu 硬件提供的 lock信號保證其原子性

四、弊端

1. ABA問題

???? CAS在操作的時候會檢查變量的值是否被更改過,如果沒有則更新值,但是帶來一個問題,最開始的值是A,接著變成B,最后又變成了A。經過檢查這個值確實沒有修改過,因為最后的值還是A,但是實際上這個值確實已經被修改過了。為了解決這個問題,在每次進行操作的時候加上一個版本號,每次操作的就是兩個值,一個版本號和某個值,A——>B——>A問題就變成了1A——>2B——>3A。在jdk中提供了AtomicStampedReference類解決ABA問題,用Pair這個內部類實現,包含兩個屬性,分別代表版本號和引用,在compareAndSet中先對當前引用進行檢查,再對版本號標志進行檢查,只有全部相等才更新值。

2. 只能保證一個共享變量的原子操作

???? 多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。從java1.5開始,JDK提供了AtomicReference類來保證引用對象之間的原子性,就可以把多個變量放在一個對象里來進行CAS操作。

3. 循環時間長CPU開銷較大

???? 在并發量比較高的情況下,如果許多線程反復嘗試更新某一個變量,卻又一直更新不成功,循環往復,會給CPU帶來很大的壓力。

總結

以上是生活随笔為你收集整理的Java并发编程—无锁互斥机制及CAS原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日本一区二区不卡在线观看 | 免费a级片在线观看 | 人妻妺妺窝人体色www聚色窝 | 欧美特级特黄aaaaaa在线看 | 鲁在线视频 | 亚洲理论中文字幕 | 色婷婷午夜 | 成人免费影视网站 | 91久久精品一区二区别 | 中国超碰 | 不卡的av在线 | 色播五月激情 | 久久的色偷偷 | 日韩女优在线观看 | 91大片免费看| 操女人逼逼视频 | 久久艹国产精品 | 亚洲草逼视频 | 人人天天夜夜 | www.黄色网 | 精品在线视频播放 | 男女羞羞在线观看 | 国产成人 综合 亚洲 | 丰满少妇中文字幕 | 久久精品韩国 | 黄色一级大片在线免费看国产一 | www.96av| 四虎综合网 | 亚洲熟妇国产熟妇肥婆 | 欧美在线不卡视频 | 亚洲一区二区免费在线观看 | 天堂国产一区二区三区 | 狼性av| 亚洲一区二区不卡在线观看 | 幸福宝在线观看 | 中文字幕激情视频 | 一区二区三区视频在线播放 | 天天躁日日躁狠狠很躁 | 69av在线播放 | 一区二区三区久久久 | 自拍偷拍日韩精品 | 精品国产一二区 | 深夜福利亚洲 | jizz内谢中国亚洲jizz | 成熟丰满熟妇高潮xxxxx视频 | 国语对白一区二区三区 | 亚洲第一视频在线 | 成人a视频| 亚洲一区二区网站 | 欧美精品v | 日批免费观看 | 欧美精品videosex极品 | 久久久久99精品成人片三人毛片 | 亚洲精品色午夜无码专区日韩 | 欧美综合国产 | 日韩成人av网站 | 波多野结衣毛片 | 婷婷色基地 | 丁香激情小说 | 欧美视频第二页 | 欧美精品在线第一页 | 亚洲成人av在线 | 黄色免费av网站 | 国精品无码一区二区三区 | 加勒比不卡视频 | 日韩一级理论片 | 亚洲免费专区 | 国产三级91 | 欧美绿帽交换xxx | 特级西西444www高清大胆免费看 | 韩国一级淫片 | 日本高清在线播放 | 亚洲性综合网 | 欧美精品偷拍 | 成人爽a毛片一区二区 | 一级黄色淫片 | 日本黄色免费网站 | 亚洲精品18在线观看 | 久久精品欧美一区二区 | 综合伊人久久 | 日韩aa | 欧日韩精品 | 色狠狠综合 | 日韩精品视频免费在线观看 | 国产精品视频你懂的 | 超碰在线中文字幕 | 爱如潮水3免费观看日本高清 | 都市激情 亚洲 | 国产免费一区二区三区最新6 | 蜜桃av噜噜一区二区三区小说 | 免费看女生隐私 | 欧美日韩精品一区二区在线播放 | 福利视频99 | 亚洲天堂黄色 | 亚洲人成电影一区二区在线 | 一区二区三区在线免费 | 久久偷看各类wc女厕嘘嘘偷窃 | 外国电影免费观看高清完整版 | 26uuu精品一区二区在线观看 |