1. 程式人生 > >02.第二階段、實戰Java高併發程式設計模式-4.無鎖

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.