文章目錄
- 簡介
- 原子操作
- 主要屬性
- compareAndSet()方法
- getAndIncrement()方法
- 總結
- 擴展
簡介
AtomicInteger是java并發包下面提供的原子類,主要操作的是int型別的整型,通過呼叫底層Unsafe的CAS等方法實作原子操作,【JDK】魔法類Unsafe
原子操作
-
原子操作是指不會被執行緒調度機制打斷的操作,這種操作一旦開始,就一直運行到結束,中間不會有任何執行緒背景關系切換,
-
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分,將整個操作視作一個整體是原子性的核心特征,
這里說的原子操作與資料庫ACID中的原子性,最大區別在于,資料庫中的原子性主要運用在事務中,一個事務之內的所有更新操作要么都成功,要么都失敗,事務是有回滾機制的,而這里說的原子操作是沒有回滾的,這是最大的區別,
主要屬性
// 獲取Unsafe的實體
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value欄位的偏移量
private static final long valueOffset;
// 靜態代碼塊就是初始化value欄位的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
// AtomicInteger.class.getDeclaredField 就是拿到AtomicInteger中value欄位,反射里面的知識
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 存盤int型別值的地方,使用volatile修飾
private volatile int value;
(1)使用int型別的value存盤值,且使用volatile修飾,volatile主要是保證可見性,即一個執行緒修改對另一個執行緒立即可見,主要的實作原理是記憶體屏障,
(2)呼叫Unsafe的objectFieldOffset()方法獲取value欄位在類中的偏移量,用于后面CAS操作時使用,
compareAndSet()方法
自定義增加的量
// AtomicInteger中的方法
public final boolean compareAndSet(int expectedValue, int newValue) {
// 前兩個引數內部自動跟我們給到,也就是當前value的遷移量
return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
}
// Unsafe中的方法
public final boolean compareAndSwapInt(Object o, long offset,
int expected,
int x) {
return theInternalUnsafe.compareAndSetInt(o, offset, expected, x);
}
public final native boolean compareAndSetInt(Object o, long offset,
int expected,
int x);
呼叫Unsafe.compareAndSwapInt()方法實作,這個方法有四個引數:
(1)操作的物件;
(2)物件中欄位的偏移量;
(3)原來的值,即期望的值;
(4)要修改的值;
可以看到,這是一個native方法,底層是使用C/C++寫的,主要是呼叫CPU的CAS指令來實作,它能夠保證只有當對應偏移量處的欄位值是期望值時才更新,即類似下面這樣的兩步操作:
if(value == expect) {
value = newValue;
}
通過CPU的CAS指令可以保證這兩步操作是一個整體,也就不會出現多執行緒環境中可能比較的時候value值是a,而到真正賦值的時候value值可能已經變成b了的問題,
getAndIncrement()方法
自增1
// AtomicInteger中的方法
public final int getAndIncrement() {
// 也就是加一操作
return unsafe.getAndAddInt(this, valueOffset, 1);
}
// Unsafe中的方法 theInternalUnsafe底層的物件
public final int getAndAddInt(Object o, long offset, int delta) {
return theInternalUnsafe.getAndAddInt(o, offset, delta);
}
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
// 這里就是CAS機制
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
getAndIncrement()方法底層是呼叫的Unsafe的getAndAddInt()方法,這個方法有三個引數:
(1)操作的物件;
(2)物件中欄位的偏移量;
(3)要增加的值;
查看Unsafe的getAndAddInt()方法的原始碼,可以看到它是先獲取當前的值,然后再呼叫compareAndSwapInt()嘗試更新對應偏移量處的值,如果成功了就跳出回圈,如果不成功就再重新嘗試,直到成功為止,這就是(CAS+自旋)的樂觀鎖機制
AtomicInteger中的其它方法幾乎都是類似的,最侄訓呼叫到Unsafe的compareAndSwapInt()來保證對value值更新的原子性
總結
(1)AtomicInteger中維護了一個使用volatile修飾的變數value,保證可見性;
(2)AtomicInteger中的主要方法最終幾乎都會呼叫到Unsafe的compareAndSwapInt()方法保證對變數修改的原子性,
(3)雖然AtomicInteger解決了原子性問題,但是它還存在ABA問題
擴展
為什么需要AtomicInteger?
public class Demo09 {
private volatile static int count = 0;
public static void main(String[] args) throws Exception{
Counter counter = new Counter();
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 1000; i1++) {
Demo09.increment();
}
}
}).start();
}
// 為了防止main執行緒提前結束
Thread.sleep(2000);
// 輸出100000
System.out.println(count);
}
public static void increment(){
count++;
}
}
- 發現count的值總是小于100000
加上volatile也無法解決這個問題,因為volatile僅有兩個作用:
(1)保證可見性,即一個執行緒對變數的修改另一個執行緒立即可見;
(2)禁止指令重排序;
這里有個很重要的問題,count++實際上是兩步操作,第一步是獲取count的值,第二步是對它的值加1,
使用volatile是無法保證這兩步不被其它執行緒調度打斷的,所以無法保證原子性,
這就引出了AtomicInteger,它的自增呼叫的是Unsafe的CAS并使用自旋保證一定會成功,它可以保證兩步操作的原子性,
public class Demo09 {
static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception{
Counter counter = new Counter();
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 1000; i1++) {
Demo09.increment();
}
}
}).start();
}
// 為了防止main執行緒提前結束
Thread.sleep(2000);
// 輸出100000
System.out.println(count);
}
public static void increment(){
// 呼叫AtomicInteger的getAndIncrement方法,它是采用CAS保證了原子性
count.getAndIncrement();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/354623.html
標籤:java
