Java中的CAS和ABA問題說明
1.CAS
1)CAS概念
CAS時(shí)Compare And Swap縮寫,即比較與交換是用于實(shí)現(xiàn)多線程同步的原子指令,它將內(nèi)存位置的內(nèi)容與給定值相比較,相同則修改內(nèi)存位置的值為新值,而整個(gè)操作是調(diào)用的UnSafe的compareAndSwapObject、compareAndSwapInt或者compareAndSwapLong完成的,而這些方法都是native修飾的本地方法,是一種系統(tǒng)原語(yǔ)系統(tǒng)支持的操作。
2)CAS產(chǎn)生的影響(無鎖執(zhí)行)
CAS是一種無鎖對(duì)象的原子操作,鎖分為樂觀鎖和悲觀鎖,樂觀派抱著幾乎不會(huì)發(fā)生修改同一資源的狀態(tài),任意操作同意對(duì)象資源,如果遇到修改同一資源的情況,資源不會(huì)修改成功,能夠保證資源的安全,而悲觀派會(huì)認(rèn)為同一資源被錯(cuò)誤修改后會(huì)造成不可挽回的局面,故自能有一個(gè)線程修改資源,這樣總會(huì)對(duì)系統(tǒng)性能產(chǎn)生一定的影響,拖慢自行速度,CAS即無鎖執(zhí)行者,被CAS修飾過的資源可以同時(shí)被多個(gè)線程修改依然能保證系統(tǒng)安全,無鎖不需要等待提高系統(tǒng)性能,jdk提供的CAS原理實(shí)現(xiàn)的并發(fā)類Automic系列運(yùn)用及其原理介紹。
3)Automic并發(fā)類CAS原理代碼分析
首先介紹java的指針操作類UnSafe,Unsafe類是在sun.misc包下,不屬于Java標(biāo)準(zhǔn)。但是很多Java的基礎(chǔ)類庫(kù),包括一些被廣泛使用的高性能開發(fā)庫(kù)都是基于Unsafe類開發(fā)的,因?yàn)閁nSafe使Java像C語(yǔ)言一樣使其擁有操作內(nèi)存指針的能力,因?yàn)椴僮鲀?nèi)存指針容易出錯(cuò),故起名UnSafe不安全的類,因此Java官方并不建議使用的,但CAS原理就是UnSafe類中的compareAndSwapObject、compareAndSwapInt和compareAndSwapLong方法實(shí)現(xiàn)的,該方法需傳入四個(gè)參數(shù):第一個(gè)參數(shù)代表給定的對(duì)象,第二個(gè)參數(shù)代表給定對(duì)象再內(nèi)存中的偏移量,第三個(gè)參數(shù)標(biāo)識(shí)對(duì)象的期望值,第四個(gè)參數(shù)標(biāo)識(shí)要修改的值,并發(fā)保重的Automic系列的原子操作類都是使用UnSafe類實(shí)現(xiàn)的。
UnSafe源碼如下:
/** * 第一個(gè)參數(shù)var1代表給定對(duì)象,第二個(gè)參數(shù)var2代表var1對(duì)象在內(nèi)存中的偏移量,第三個(gè)參數(shù)var3為期望修改* 的對(duì)象舊值,第四個(gè)參數(shù)var4代表要修改的值或著說是修改后的值。 **/ public final native boolean compareAndSwapObject(Object var1, long var2, Object var3, Object var4); ? ? public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); ? ? public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
舉例AtomicInteger源碼實(shí)現(xiàn)原理:
AutomicInteger中的getAndSet實(shí)現(xiàn)原理解析:
? ? /**
? ? * 調(diào)用的UnSafe的getAndSetInt方法,給定值和偏移量和修改的值,
? ? * 獲取修改的值var5作為compareAndSwapInt的第三個(gè)參數(shù)用來和var1比較相同則執(zhí)行更新操作
? ? * while循環(huán)知道操作成功。
? ? *public final int getAndSetInt(Object var1, long var2, int var4) {
? ? * ? int var5;
? ? * ? do {
? ? * ? ? ? var5 = this.getIntVolatile(var1, var2);
? ? * ? } while(!this.compareAndSwapInt(var1, var2, var5, var4));
? ? *
? ? * ? ?return var5;
? ? *}
? ? **/
? ? public final int getAndSet(int newValue) {
? ? ? ? return unsafe.getAndSetInt(this, valueOffset, newValue);
? ? }package java.util.concurrent.atomic;
import java.util.function.IntUnaryOperator;
import java.util.function.IntBinaryOperator;
import sun.misc.Unsafe;
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 獲取UnSafe對(duì)象實(shí)例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//對(duì)象在內(nèi)存中的偏移量
private static final long valueOffset;
//初始化valueOffset
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//對(duì)象屬性值
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
public AtomicInteger() {
}
/**
* 調(diào)用的UnSafe的getAndSetInt方法,給定值和偏移量和修改的值,
* 獲取修改的值var5作為compareAndSwapInt的第三個(gè)參數(shù)用來和var1比較相同則執(zhí)行更新操作
* while循環(huán)知道操作成功。
*public final int getAndSetInt(Object var1, long var2, int var4) {
* int var5;
* do {
* var5 = this.getIntVolatile(var1, var2);
* } while(!this.compareAndSwapInt(var1, var2, var5, var4));
*
* return var5;
*}
**/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
//調(diào)用UnSafe的compareAndSwapInt方法保證CAS
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//調(diào)用UnSafe的compareAndSwapInt方法保證CAS
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//調(diào)用UnSafe的getAndAddInt再調(diào)用UnSafe的getAndSetInt方法保證CAS
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
.........
}4)CAS導(dǎo)致的ABA問題
操作對(duì)象,獲取對(duì)象后,執(zhí)行CAS操作前,被其他線程修改后,且又修改為原來的對(duì)象值,導(dǎo)致CAS忽略其他線程的修改,成功執(zhí)行CAS對(duì)象修改,這種情況就叫做ABA問題。
下圖所示:

解決辦法:
AtomicStampedReference類提供了解決辦法,在對(duì)象之中又添加了stamp時(shí)間戳屬性避免其他線程修改了多次并變回修改前的value值,但對(duì)比stamp不同便可知道對(duì)象是被修改過的,只有提供屬性值和stamp時(shí)間戳相等才能成功執(zhí)行CAS修改操作,里面包裹了一個(gè)鍵值對(duì)對(duì)象AtomicStampedReference.Pair<V> pair類型,pair中值為屬性值,value為stamp時(shí)間戳,在執(zhí)行CAS操作時(shí)需要提供原值的value和時(shí)間戳都相等的情況才能成功執(zhí)行CAS操作。
AtomicMarkableReference類提供了解決辦法,在對(duì)象之中又添加了stamp時(shí)間戳屬性避免其他線程修改了多次并變回修改前的value值,但對(duì)比stamp不同便可知道對(duì)象是被修改過的,只有提供屬性值和boolean類型的mark標(biāo)記相等才能成功執(zhí)行CAS修改操作,里面包裹了一個(gè)鍵值對(duì)對(duì)象AtomicMarkableReference.Pair<V> pair類型,pair中值為屬性值,value為mark是否被修改的標(biāo)記,在執(zhí)行CAS操作時(shí)需要提供原值的value和mark標(biāo)記都相等的情況才能成功執(zhí)行CAS操作。
本文只介紹AtomicStampedReference類的源碼分析,AtomicMarkableReference類同AtomicStampedReference類原理一樣,
源碼如下:
package java.util.concurrent.atomic;
public class AtomicStampedReference<V> {
/**
* 對(duì)象值時(shí)一個(gè)AtomicStampedReference內(nèi)置對(duì)象Pair,包裹了reference和stamp兩個(gè)屬性
*/
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
/**
* 初始化對(duì)象并初始化pair
*/
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
/**
* 比較當(dāng)前對(duì)象屬性值和輸入原始值為真,在比較當(dāng)前對(duì)象的時(shí)間stamp與期望的stamp進(jìn)行比較
* 如果也想等,就更新值和stamp
* @param expectedReference 原始值
* @param newReference 新值
* @param expectedStamp 期望時(shí)間
* @param newStamp 新時(shí)間
* @return {@code true} if successful
*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair; //賦值當(dāng)前對(duì)象
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
String field, Class<?> klazz) {
try {
return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
} catch (NoSuchFieldException e) {
// Convert Exception to corresponding Error
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}
}以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java創(chuàng)建多線程的幾種方式實(shí)現(xiàn)
這篇文章主要介紹了Java創(chuàng)建多線程的幾種方式實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Spring Cloud實(shí)戰(zhàn)技巧之使用隨機(jī)端口
這篇文章主要給大家介紹了關(guān)于Spring Cloud實(shí)戰(zhàn)技巧之使用隨機(jī)端口的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-06-06
詳解Spring?Boot中@PostConstruct的使用示例代碼
在Java中,@PostConstruct是一個(gè)注解,通常用于標(biāo)記一個(gè)方法,它表示該方法在類實(shí)例化之后(通過構(gòu)造函數(shù)創(chuàng)建對(duì)象之后)立即執(zhí)行,這篇文章主要介紹了詳解Spring?Boot中@PostConstruct的使用,需要的朋友可以參考下2023-09-09
java 教你如何給你的頭像添加一個(gè)好看的國(guó)旗
這篇文章主要介紹了java 教你如何給你的頭像添加一個(gè)好看的國(guó)旗,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Java聊天室之實(shí)現(xiàn)接收和發(fā)送Socket
這篇文章主要為大家詳細(xì)介紹了Java簡(jiǎn)易聊天室之實(shí)現(xiàn)接收和發(fā)送Socket功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以了解一下2022-10-10
關(guān)于web項(xiàng)目讀取classpath下面文件的心得分享
這篇文章主要介紹了關(guān)于web項(xiàng)目讀取classpath下面文件的心得,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
java并發(fā)編程專題(二)----如何創(chuàng)建并運(yùn)行java線程
這篇文章主要介紹了java并發(fā)編程如何創(chuàng)建并運(yùn)行java線程,文中講解非常詳細(xì),示例代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06

