Java中的CAS鎖機制(無鎖、自旋鎖、樂觀鎖、輕量級鎖)詳解
什么是CAS機制(compare and swap)
CAS算法的作用:解決多線程條件下使用鎖造成性能損耗問題的算法,保證了原子性,這個原子操作是由CPU來完成的
CAS的原理:CAS算法有三個操作數(shù),通過內(nèi)存中的值(V)、預(yù)期原始值(A)、修改后的新值。
(1)如果內(nèi)存中的值和預(yù)期原始值相等, 就將修改后的新值保存到內(nèi)存中。
(2)如果內(nèi)存中的值和預(yù)期原始值不相等,說明共享數(shù)據(jù)已經(jīng)被修改,放棄已經(jīng)所做的操作,然后重新執(zhí)行剛才的操作,直到重試成功。
注意:
(1)預(yù)期原始值(A)是從偏移位置讀取到三級緩存中讓CPU處理的值,修改后的新值是預(yù)期原始值經(jīng)CPU處理暫時存儲在CPU的三級緩存中的值,而內(nèi)存指定偏移位置中的原始值。
(2)比較從指定偏移位置讀取到緩存的值與指定內(nèi)存偏移位置的值是否相等,如果相等則修改指定內(nèi)存偏移位置的值,這個操作是操作系統(tǒng)底層匯編的一個原子指令實現(xiàn)的,保證了原子性
- JVM中CAS是通過UnSafe類來調(diào)用操作系統(tǒng)底層的CAS指令實現(xiàn)。
- CAS基于樂觀鎖思想來設(shè)計的,其不會引發(fā)阻塞,synchronize會導(dǎo)致阻塞。
原子類
java.util.concurrent.atomic包下的原子類都使用了CAS算法。而java.util.concurrent中的大多數(shù)類的實現(xiàn)都直接或間接的使用了這些原子類。 Unsafe類使Java擁有了類似C語言指針操作內(nèi)存空間的能力,同時也帶來了指針的安全問題。
AtomicInteger原子類
AtomicInteger等原子類沒有使用synchronized鎖,而是通過volatile和CAS(Compare And Swap)解決資源的線程安全問題。
(1)volatile保證了可見性和有序性
(2)CAS保證了原子性,而且是無鎖操作,提高了并發(fā)效率。
//創(chuàng)建Unsafe類的實例 private static final Unsafe unsafe = Unsafe.getUnsafe(); //成員變量value是在內(nèi)存地址中距離當(dāng)前對象首地址的偏移量, 具體賦值是在下面的靜態(tài)代碼塊中中進行的 private static final long valueOffset; static { try { //類加載的時候,在靜態(tài)代碼塊中獲取變量value的偏移量 valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } // 當(dāng)前AtomicInteger原子類的value值 private volatile int value; //類似于i++操作 public final int getAndIncrement() { //this代表當(dāng)前AtomicInteger類型的對象,valueOffset表示value成員變量的偏移量 return unsafe.getAndAddInt(this, valueOffset, 1); } ================================上方為AtomicInteger類中的方法,下方為Unsafe類中的方法========================================================= //此方法的作用:獲取內(nèi)存地址為原子對象首地址+原子對象value屬性地址偏移量, 并將該變量值加上delta public final int getAndAddInt(Object obj, long offset, int delta) { int v; do { //通過對象和偏移量獲取變量值作為期望值,在修改該內(nèi)存偏移位置的值時與原始進行比較 //此方法中采用volatile的底層原理,保證了內(nèi)存可見性,所有線程都從內(nèi)存中獲取變量vlaue的值,所有線程看到的值一致。 v= this.getIntVolatile(obj, offset); /* while中的compareAndSwapInt()方法嘗試修改v的值,具體地, 該方法也會通過obj和offset獲取變量的值 如果這個值和v不一樣, 說明其他線程修改了obj+offset地址處的值, 此時compareAndSwapInt()返回false, 繼續(xù)循環(huán) 如果這個值和v一樣, 說明沒有其他線程修改obj+offset地址處的值, 此時可以將obj+offset地址處的值改為v+delta, compareAndSwapInt()返回true, 退出循環(huán) Unsafe類中的compareAndSwapInt()方法是原子操作, 所以compareAndSwapInt()修改obj+offset地址處的值的時候不會被其他線程中斷 */ } while(!this.compareAndSwapInt(obj, offset, v, v + delta)); return v; }
操作步驟:
(1)獲取AtomicInteger對象首地址指定偏移量位置上的值,作為期望值。
(2)取出獲取AtomicInteger對象偏移量上的值,判斷與期望值是否相等,相等就修改AtomicInteger在內(nèi)存偏移量上的值,不相等就返回false,重新執(zhí)行第一步操作,重新獲取內(nèi)存指定偏移量位置的值。
(3) 如果相等,則修改值并返回true。
注意:從1、2步可以看CAS機制實現(xiàn)的鎖是自旋鎖,如果線程一直無法獲取到鎖,則一直自旋,不會阻塞
CAS和syncronized的比較
CAS線程不會阻塞,線程一致自旋 syncronized會阻塞線程,會進行線程的上下文切換,會由用戶態(tài)切換到內(nèi)核態(tài),切換前需要保存用戶態(tài)的上下文,而內(nèi)核態(tài)恢復(fù)到用戶態(tài),又需要恢復(fù)保存的上下文,非常消耗資源。
CAS的缺點
(1)ABA問題 如果一個線程t1正修改共享變量的值A(chǔ),但還沒修改,此時另一個線程t2獲取到CPU時間片,將共享變量的值A(chǔ)修改為B,然后又修改為A,此時線程t1檢查發(fā)現(xiàn)共享變量的值沒有發(fā)生變化,但是實際上卻變化了。
解決辦法: 使用版本號,在變量前面追加上版本號,每次變量更新的時候把版本號加1,那么A-B-A 就會變成1A-2B-3A。從Java1.5開始JUC包里提供了一個類AtomicStampedReference來解決ABA問題。AtomicStampedReference類的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前版本號是否等于預(yù)期版本號,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。
(2)循環(huán)時間長開銷會比較大:自旋重試時間,會給CPU帶來非常大的執(zhí)行開銷
(3)只能保證一個共享變量的原子操作,不能保證同時對多個變量的原子性操作 解決辦法: 從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行CAS操作
CAS使用注意事項
(1)CAS需要和volatile配合使用
CAS只能保證變量的原子性,不能保證變量的內(nèi)存可見性。CAS獲取共享變量的值時,需要和volatile配合使用,來保證共享變量的可見性
(2)CAS適用于并發(fā)量不高、多核CPU的情況
CPU多核情況下可以同時執(zhí)行,如果不合適就失敗。而并發(fā)量過高,會導(dǎo)致自旋重試耗費大量的CPU資源
到此這篇關(guān)于Java中的CAS鎖機制(無鎖、自旋鎖、樂觀鎖、輕量級鎖)詳解的文章就介紹到這了,更多相關(guān)CAS鎖機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java使用單向鏈表解決數(shù)據(jù)存儲自定義排序問題
本文主要介紹了java使用單向鏈表解決數(shù)據(jù)存儲自定義排序問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03Java使用modbus-master-tcp實現(xiàn)modbus tcp通訊
這篇文章主要為大家詳細(xì)介紹了另外一種Java語言的modbux tcp通訊方案,那就是modbus-master-tcp,文中的示例代碼講解詳細(xì),需要的可以了解下2023-12-12SpringCloud?Function?SpEL注入漏洞分析及環(huán)境搭建
SpringCloud 是一套分布式系統(tǒng)的解決方案,常見的還有阿里巴巴的Dubbo,F(xiàn)ass的底層實現(xiàn)就是函數(shù)式編程,SpringCloud Function 就是Spring提供的分布式函數(shù)式編程組件,下面給大家介紹下SpringCloud?Function?SpEL注入漏洞分析,感興趣的朋友一起看看吧2022-04-04詳解Spring Boot 使用Spring security 集成CAS
本篇文章主要介紹了詳解Spring Boot 使用Spring security 集成CAS,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Java中replace、replaceAll和replaceFirst函數(shù)的用法小結(jié)
相信會java的同學(xué)估計都用過replace、replaceAll、replaceFirst這三個函數(shù),可是,我們真的懂他們嗎?下面通過這篇文章大家再來好好學(xué)習(xí)學(xué)習(xí)下這幾個函數(shù)。2016-09-09解析Spring中@Controller@Service等線程安全問題
這篇文章主要為大家介紹解析了Spring中@Controller@Service等線程的安全問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03