02.第二階段、實戰Java高併發程式設計模式-4.無鎖
1.1. CAS
CAS演算法的過程是這樣:它包含3個引數CAS(V,E,N)。V表示要更新的變數,E表示預期值,N表示新值。僅當V 值等於E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他執行緒做了更新,則當前執行緒什麼 都不做。最後,CAS返回當前V的真實值。CAS操作是抱著樂觀的態度進行的,它總是認為自己可以成功完成 操作。當多個執行緒同時使用CAS操作一個變數時,只有一個會勝出,併成功更新,其餘均會失敗。失敗的執行緒 不會被掛起,僅是被告知失敗,並且允許再次嘗試,當然也允許失敗的執行緒放棄操作。基於這樣的原理,CAS 操作即時沒有鎖,也可以發現其他執行緒對當前執行緒的干擾,並進行恰當的處理。
1.2. CPU指令
cmpxchg
/*
accumulator = AL, AX, or EAX, depending on whether
a byte, word, or doubleword comparison is being performed */
if(accumulator == Destination) {
ZF = 1;
Destination = Source; }
else {
ZF = 0;
accumulator = Destination; }
2. 無鎖類的使用
2.1. AtomicInteger
2.1.1. 概述 Number
2.1.2. 主要介面
public |
final |
int |
get() |
//取得當前值 //設定當前值
//設定新值,並返回舊值
public |
final |
void |
set(int |
newValue) |
public |
final |
int |
getAndSet(int |
newValue) |
public |
final |
boolean |
compareAndSet(int |
expect, |
int |
u) |
//如果當前值為expect,則設定為u
public |
final |
int |
getAndIncrement() |
//當前值加1,返回舊值 //當前值減1,返回舊值
//當前值增加delta,返回舊值 //當前值加1,返回新值 //當前值減1,返回新值
//當前值增加delta,返回新值
public |
final |
int |
getAndDecrement() |
public |
final |
int |
getAndAdd(int |
delta) |
public |
final |
int |
incrementAndGet() |
public |
final |
int |
decrementAndGet() |
public |
final |
int |
addAndGet(int |
delta) |
2.1.3. 主要介面的實現
2.2. Unsafe
2.2.1. 概述
非安全的操作,比如: 根據偏移量設定值 park() 底層的CAS操作
非公開API,在不同版本的JDK中, 可能有較大差異
2.2.2. 主要介面
//獲得給定物件偏移量上的int值
public native int getInt(Object o, long offset); //設定給定物件偏移量上的int值
public native void putInt(Object o, long offset, int x); //獲得欄位在物件中的偏移量
public native long objectFieldOffset(Field f); //設定給定物件的int值,使用volatile語義
public native void putIntVolatile(Object o, long offset, int x); //獲得給定物件物件的int值,使用volatile語義
public native int getIntVolatile(Object o, long offset); //和putIntVolatile()一樣,但是它要求被操作欄位就是volatile型別的 public native void putOrderedInt(Object o, long offset, int x);
2.3. AtomicReference
2.3.1. 概述
對引用進行修改 是一個模板類,抽象化了資料型別
2.3.2. 主要介面
get()
set(V) compareAndSet() getAndSet(V)
2.4. AtomicStampedReference
2.4.1. 概述
ABA問題
2.4.2. 主要介面
//比較設定 引數依次為:期望值 寫入新值 期望時間戳 新時間戳
public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) //獲得當前物件引用
public V getReference()
//獲得當前時間戳
public int getStamp()
//設定當前物件引用和時間戳
public void set(V newReference, int newStamp)
2.5. AtomicIntegerArray
2.5.1. 概述
支援無鎖的陣列
2.5.2. 主要介面
//獲得陣列第i個下標的元素
public final int get(int i)
//獲得陣列的長度
public final int length()
//將陣列第i個下標設定為newValue,並返回舊的值
public final int getAndSet(int i, int newValue) //進行CAS操作,如果第i個下標的元素等於expect,則設定為update,設定成功返回true public final boolean compareAndSet(int i, int expect, int update)
//將第i個下標的元素加1
public final int getAndIncrement(int i) //將第i個下標的元素減1
public final int getAndDecrement(int i) //將第i個下標的元素增加delta(delta可以是負數) public final int getAndAdd(int i, int delta)
2.6. AtomicIntegerFieldUpdater
2.6.1. 概述
讓普通變數也享受原子操作
2.6.2. 主要介面 AtomicIntegerFieldUpdater.newUpdater()
incrementAndGet()
2.6.3. 小說明
1. Updater只能修改它可見範圍內的變數。因為Updater使用反射得到這個變數。如果變數不可見,就會出錯。
比如如果score申明為private,就是不可行的。
2. 為了確保變數被正確的讀取,它必須是volatile型別的。如果我們原有程式碼中未申明這個型別,那麼簡單得 申明一下就行,這不會引起什麼問題。
3. 由於CAS操作會通過物件例項中的偏移量直接進行賦值,因此,它不支援static欄位(Unsafe. objectFieldOffset()不支援靜態變數)。
3. 無鎖演算法詳解
3.1.