一、概述
AtomicInteger類處於java.util.concurrent.atomic包下,與其他原子操作的類一樣,底層都是採用CAS機制,調用了Unsafe類的CAS方法實現的。我們先分析一下AtomicInteger類的源碼,再來分析CAS機制的種種。
二、源碼分析
1. 類的聲明
public class AtomicInteger extends Number implements java.io.Serializable
繼承了 Number, 這主要是提供方法將數值轉化爲 byte, double 等方便 Java 開發者使用; 實現了 Serializable, 爲了網絡傳輸等的序列化用, 編碼時最好手動生成序列化 ID, 讓 javac 編譯器生成開銷大, 而且可能造成意想不到的狀況。
2. 成員變量
//序列化標識id
private static final long serialVersionUID = 6214790243416807050L;
//Unsafe類的一個實例,提供一些不安全的操作的方法用於直接操作內存,一般不會在自己的程序中使用該類
//在這裏主要用到其中的objectFieldOffset、putOrderedInt、compareAndSwapInt方法
private static final Unsafe unsafe = Unsafe.getUnsafe();
//value成員屬性的內存地址相對於對象內存地址的偏移量
private static final long valueOffset;
////初始化valueOffset,通過unsafe.objectFieldOffset方法獲取成員屬性value內存地址相對於對象內存地址的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//int的值,volatile修飾,保證線程之間的可見性
private volatile int value;
Unsafe類是一個JDK內部使用的專屬類,我們自己的應用程序無法直接使用Unsafe類。通過觀察源碼,可以知道獲得Unsafe實例的方法是調動其工廠方法getUnsafe(),源碼如下:
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
它會檢查調用getUnsafe()函數的類,判斷調用這的類加載器是否是系統類加載器(系統類加載器爲null),如果不是就直接拋出異常,拒絕工作,這也是爲什麼我們寫的程序沒法直接使用這個類的原因。
3. 構造函數
//int參數類型的構造函數
public AtomicInteger(int initialValue) {
value = initialValue;
}
//無參構造函數,初始值是0
public AtomicInteger() {
}
4. 其他方法
/**
* 獲取int值
*/
public final int get() {
return value;
}
/**
* 設爲指定值
*/
public final void set(int newValue) {
value = newValue;
}
/**
* 最終設爲指定值,但其它線程不能馬上看到變化,會延時一會
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* 以原子方式設置爲給定值,並返回舊值
*/
public final int getAndSet(int newValue) {
//樂觀鎖,非阻塞同步方式,循環調用compareAndSet,也就是自旋,直到成功
for (;;) {
int current = get();
//CAS操作,期待值current與內存中的值比較,相等的話設爲newValue值
//否則下個循環,調用get()獲取current值,繼續執行CAS操作直到成功
if (compareAndSet(current, newValue))
return current;
}
}
/**
* CAS操作,現代CPU已廣泛支持,是一種原子操作;
* 簡單地說,當期待值expect與valueOffset地址處的值相等時,設置爲update值
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 弱比較
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//調用unsafe.getAndAddInt方法進行數值操作
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
//調用unsafe.getAndAddInt方法進行數值操作
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
//調用unsafe.getAndAddInt方法進行數值操作
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//調用unsafe.getAndAddInt方法進行數值操作
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
//調用unsafe.getAndAddInt方法進行數值操作
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
/**
* 原子操作,自增,返回舊值
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,自減,返回舊值
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,加上一個數,返回舊值
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,自增,返回新值
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子操作,自減,返回新值
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子操作,加上一個數,返回新值
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
/**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
}
public int intValue() {
return get();
}
public long longValue() {
return (long)get();
}
public float floatValue() {
return (float)get();
}
public double doubleValue() {
return (double)get();
}
}
5. getAndAddInt()方法
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
第一個參數var1爲給定的對象,var2爲對象內的偏移量(其實就是一個字段到對象頭部的偏移量,通過這個偏移量可以快速定位字段),var4表示期望值,var5表示要設置的值。如果指定的字段的值等於var4,那麼就會把它設置爲var5。
6. CAS機制原理
CAS全拼又叫做compareAndSwap,從名字上的意思就知道是比較交換的意思。它包含 3 個參數 CAS(V,E,N),V表示要更新變量的值,E表示預期值,N表示新值。僅當 V值等於E值時,纔會將V的值設爲N,如果V值和E值不同,則說明已經有其他線程做兩個更新,則當前線程則什麼都不做。最後,CAS 返回當前V的真實值。
7. CAS的優勢
CAS是一種樂觀鎖,而且是一種非阻塞的輕量級的樂觀鎖。當一個線程想要獲得鎖,對方會給一個迴應表示這個鎖能不能獲得。在資源競爭不激烈的情況下性能高,相比synchronized重量鎖,synchronized會進行比較複雜的加鎖,解鎖和喚醒操作。
8. CAS存在的問題
(1) ABA問題
* 問題描述:CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。
* 解決辦法:
a. 用CAS的一個變種DCAS,DCAS是對於每一個V增加一個引用的表示修改次數的標記符。對於每個V,如果引用修改了一次,這個計數器就加1,然後再這個變量需要更新的時候,就同時檢查變量的值和計數器的值。都符合條件纔可以修改V值。
b. 通過將標記與要進行CAS操作的每個值相關聯,並原子地更新值和標記。也就是一旦V第一次被使用,就不會再重複使用,如有需要則分配新的V。垃圾收集器可以檢查V,保證其不被循環使用,直到當前的訪問操作全部結束。使用 AtomicStampedReference 來解決CAS中的ABA問題,它不再像compareAndSet方法 中只比較內存中的值也當前值是否相等,而且先比較引用是否相等,然後比較值是否相等。
c. 設置一個版本號,每次修改都會修改版本號,每次對比的時候不僅對比值,還要對比版本號來保證數據沒被修改。
(2) 自旋時間長導致開銷大
* 問題描述:自旋 CAS 如果長時間不成功,會給 CPU 帶來非常大的執行開銷。
* 解決辦法:如果JVM能支持處理器提供的 pause 指令那麼效率會有一定的提升,pause 指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使 CPU 不會消耗過多的執行資源,延遲的時間取決於具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序衝突(memory order violation:內存順序衝突一般是由僞/假共享引起,假共享是指多個 CPU 同時修改同一個緩存行的不同部分而引起其中一個CPU的操作無效,當出現這個內存順序衝突時,CPU必須清空流水線)而引起 CPU 流水線被清空(CPU pipeline flush),從而提高 CPU 的執行效率。
三、總結
通過本篇博文,對原子包java.util.concurrent.atomic下的AtomicInteger類進行了分析,其他的類也比較類似,都是底層採用了CAS機制完成各種操作。也學習了CAS的原理和存在的問題以及解決辦法。
更多精彩內容,敬請掃描下方二維碼,關注我的微信公衆號【Java覺淺】,獲取第一時間更新哦!