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)代碼塊中中進(jìn)行的
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)存偏移位置的值時與原始進(jìn)行比較
//此方法中采用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會阻塞線程,會進(jìn)行線程的上下文切換,會由用戶態(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類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進(jìn)行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)文章
Struts2學(xué)習(xí)教程之?dāng)r截器機制與自定義攔截器
這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)基礎(chǔ)教程之?dāng)r截器機制與自定義攔截器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
使用springboot整合websocket實現(xiàn)群聊教程
websocket怎么說呢,就是服務(wù)器可以主動向客戶端發(fā)起對話,下面就是springboot整合websocket實現(xiàn)群聊的操作代碼,一起來看一下get新技能吧2021-08-08
使用nacos命名空間namespace用法,測試時做實例隔離
Nacos命名空間用于管理多套不同環(huán)境的服務(wù)器,增加一個命名空間的概念,可以用一套Nacos注冊中心管理多套不同的環(huán)境2024-12-12
解決idea services窗口不見的一種特殊情況(小白采坑系列)
這篇文章主要介紹了解決idea services窗口不見的一種特殊情況(小白采坑系列),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09

