介紹
AtomicInteger 是 JDK1.5 之後引入的原子更新整形數據的工具。同期引入的還有另外 11 個原子操作工具,這個工具都能提供簡單高效並且多線程安全的原子操作。
原理
AtomicInteger 能夠保證線程安全的原因是 AtomicInteger 的整形屬性 private volatile int value 使用 volatile 關鍵詞修飾,能夠保證該屬性對各個線程都是最新的值;其次 value 的修改是利用 unsafe 的方法修改 value 的值,而 unsafe 的方式修改能夠保證原子性。
原子性操作是指不會被線程調度機制打斷的操作(來自百度百科),即線程切換還麼在原子操作之前,要麼在原子操作之後,如果操作失敗,那麼一起回滾。在多核環境下,原子操作需要操作系統控制一個 CPU 在操作某一個內存時,其他 CPU 不會操作這個內存空間。
volatile 修飾的屬性具備:
- 可見性:對於任何線程讀取 volatile 變量,該線程總能看到其他線程對該變量的最後的寫入值。任何線程的寫入都直接推到主內存中,任何線程讀取都會拉取主內存的值(爲了非 volatile 修飾的變量,更新和讀取都針對的是線程內存中的拷貝,不會立即推到主內存中,此時其他線程無法知曉該工作線程對該共享數據的修改)。
- 原子性:volatile 修飾的變量在做賦值操作時具備原子性(i++這種不具備原子性)。比如 volatile 修改的 long 和 double 類型不會被拆分成高低 32 位交由多個 CPU 處理。
CAS 操作的原子性:
以 compareAndSwapInt 爲例,代碼實現如下:
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
來自《Java 併發編程的藝術》
處理器會保證基本的內存操作的原子性,在多核下處理器使用基於緩存加鎖或者總線加鎖的方式來實現原子操作。
- 總線加鎖是指處理器提供一個 LOCK#信號,當一個處理器在總線上輸出此信號時,其他處理器的請求會被阻塞,那麼該處理器就可以獨佔內存。
- 緩存加鎖是指內存區域如果被緩存到處理器的緩存行中,並且在 Lock 操作期間被鎖定,那麼當它執行鎖操作回寫到內存時,處理器不在總線上聲明 LOCK#信號,而是修改內部的內存地址,並允許它的緩存一致性機制來保證操作的原子性。緩存一致性機制會阻止同時修改兩處及以上的處理器緩存的內存數據,當其他處理器回寫已被鎖定的緩存行的數據時,會使得其他處理器的緩存航無效。這種機制使得其他處理器在讀寫該緩存航數據時只能從內存中獲取到最新的數據後再進行操作。
cmpxchg 指令會聲明 LOCK#信號對內存區域加鎖保證原子性。
方法列表
Number 方法實現
/**
* 返回 value 屬性值
*/
public int intValue() {
return get();
}
/**
* 將 value 屬性值轉成 long 類型返回
*/
public long longValue() {
return (long)get();
}
/**
* 將 value 屬性值轉成 float 類型返回
*/
public float floatValue() {
return (float)get();
}
/**
* 將 value 屬性值轉成 double 類型返回
*/
public double doubleValue() {
return (double)get();
}
AtomicInteger 操作整形數據的方法列表
// AtomicInteger 類包裝的整型數據
private volatile int value;
/**
* 構造方法
*
* @param initialValue 整型數據初始值
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* 構造方法
* 整型數據的初始值爲 0
*/
public AtomicInteger() {
}
/**
* 返回當前的整型數據
*
* @return 整型數據
*/
public final int get() {
return value;
}
/**
* 修改整型數據
*
* @param newValue 修改後的整型數據數值
*/
public final void set(int newValue) {
value = newValue;
}
/**
* 修改整型數據
* 該方法不會立即推送到主內存。該方法性能高但是不能保證線程安全,需要在其他保證線程安全的技術手段配合保證線程安全下使用該方法。
*
* @param newValue 修改後的整型數值
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* 將數值改成指定的值,並返回修改前的數值。
*
* @param newValue 修改後的數值
* @return 修改前的數值
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
/**
* 比較修改
* 如果和預期的值相同則修改成指定的值。修改成功返回 true,否則返回 false
*
* @param expect 預期的現在數值
* @param update 修改後的數值
* @return 如果修改成功返回 true,修改失敗返回 false
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 比較修改
* 該方法在修改變量前後不會加入內存屏障,即該方法修改的變量不會立刻推到主內存中,也不會讓其他線程的這個變量內存失效。
* 該方法比 compareAndSet 的性能要好。
*
* @param expect 預期值
* @param update 修改後的值
* @return 修改成功則返回 true,否則返回 false
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 原有值基礎上加一
*
* @return 返回加一前的數值
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
* 原有值基礎上減一
*
* @return 減一操作前的數值
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
/**
* 原有值基礎上加上指定的數值
*
* @param delta 增加的數值
* @return 加之前的數值
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
/**
* 原有值基礎上加一併返回加一後的數值
*
* @return 加一後的結果
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
/**
* 原有值減一併返回減一後的結果
*
* @return 減一後的結果
*/
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
/**
* 原有值增加指定的數值並返回增加後的結果
* @param delta 指定需要增加的數值
* @return 增加後的結果
*/
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}