一文帶你學會AQS和併發工具類的關係
技術標籤:objective-c
AQS(AbstractQueuedSynchronizer)是JAVA中眾多鎖以及併發工具的基礎,其底層採用樂觀鎖,大量使用了CAS操作, 並且在衝突時,採用自旋方式重試,以實現輕量級和高效地獲取鎖。
提供一個框架,用於實現依賴於先進先出(FIFO)等待佇列的阻塞鎖和相關的同步器(semaphore等)。 此類旨在為大多數依賴單個原子int值表示狀態的同步器提供有用的基礎。 子類必須定義更改此狀態的受保護方法,並定義該狀態對於獲取或釋放此物件而言意味著什麼。 鑑於這些,此類中的其他方法將執行所有排隊和阻塞機制。 子類可以維護其他狀態欄位,但是相對於同步,僅跟蹤使用方法getState,setState和compareAndSetState操作的原子更新的int值。
此類支援預設獨佔模式和共享模式之一或兩者。 當以獨佔方式進行獲取時,其他執行緒嘗試進行的獲取將無法成功。 由多個執行緒獲取的共享模式可能(但不一定)成功。 該類不“理解”這些差異,當共享模式獲取成功時,下一個等待執行緒(如果存在)還必須確定它是否也可以獲取。 在不同模式下等待的執行緒共享相同的FIFO佇列。 通常,實現子類僅支援這些模式之一,但例如可以在ReadWriteLock發揮作用。 僅支援獨佔模式或僅支援共享模式的子類無需定義支援未使用模式的方法。
- 核心知識點
2.1 state
private volatile int state; // 同步狀態
state是整個工具的核心,通常整個工具都是在設定和修改狀態,很多方法的操作都依賴於當前狀態是什麼。由於狀態是全域性共享的,一般會被設定成volatile型別,為了保證其修改的可見性;
2.2 CLH佇列
AQS中的佇列是CLH變體的虛擬雙向佇列(FIFO),AQS是通過將每條請求共享資源的執行緒封裝成一個節點來實現鎖的分配。佇列採用的是悲觀鎖的思想,表示當前所等待的資源,狀態或者條件短時間內可能無法滿足。因此,它會將當前執行緒包裝成某種型別的資料結構,扔到一個等待佇列中,當一定條件滿足後,再從等待佇列中取出。
2.3 CAS操作
CAS操作是最輕量的併發處理,通常我們對於狀態的修改都會用到CAS操作,因為狀態可能被多個執行緒同時修改,CAS操作保證了同一個時刻,只有一個執行緒能修改成功,從而保證了執行緒安全。CAS採用的是樂觀鎖的思想,因此常常伴隨著自旋,如果發現當前無法成功地執行CAS,則不斷重試,直到成功為止。
- 核心實現原理
3.1 作為同步器的基礎
要將此類用作同步器的基礎,請使用getState,setState或compareAndSetState檢查或修改同步狀態,以重新定義以下方法(如適用):
tryAcquire
獨佔方式,arg為獲取鎖的次數,嘗試獲取資源,成功則返回True,失敗則返回False。
tryRelease
獨佔方式,arg為釋放鎖的次數,嘗試釋放資源,成功則返回True,失敗則返回False。
tryAcquireShared
共享方式,arg為獲取鎖的次數,嘗試獲取資源。負數表示失敗;0表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源。
tryReleaseShared
共享方式,arg為釋放鎖的次數,嘗試釋放資源,如果釋放後允許喚醒後續等待結點返回True,否則返回False。
isHeldExclusively
該執行緒是否正在獨佔資源。只有用到Condition才需要去實現它。
預設情況下,這些方法中的每一個都會引發UnsupportedOperationException 。 這些方法的實現必須在內部是執行緒安全的,並且通常應簡短且不阻塞。 定義這些方法是使用此類的唯一受支援的方法。 所有其他方法都被宣告為final方法,因為它們不能獨立變化。
3.2 同步狀態state
AQS中維護了一個名為state的欄位,意為同步狀態,是由volatile修飾的,用於展示當前臨界資源的獲鎖情況。
private volatile int state;
下面提供了幾個訪問這個state欄位的方法:
返回同步狀態的當前值。 此操作具有volatile讀取的記憶體語義
protected final int getState() {
return state;
}
設定同步狀態的值。 此操作具有volatile寫操作的記憶體語義。
protected final void setState(int newState) {
state = newState;
}
如果當前狀態值等於期望值,則以原子方式將同步狀態設定為給定的更新值。 此操作具有volatile讀寫的記憶體語義
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
這幾個方法都是Final修飾的,說明子類中無法重寫它們。我們可以通過修改State欄位表示的同步狀態來實現多執行緒的獨佔模式和共享模式
state的值即表示了鎖的狀態,state為0表示鎖沒有被佔用,state大於0表示當前已經有執行緒持有該鎖,這裡之所以說大於0而不說等於1是因為可能存在可重入的情況。你可以把state變數當做是當前持有該鎖的執行緒數量。
public abstract class AbstractOwnableSynchronizer
protected AbstractOwnableSynchronizer() {
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
exclusiveOwnerThread 屬性的值即為當前持有鎖的執行緒獨佔模式獲取鎖流程:
圖片
共享模式獲取鎖流程:
圖片
3.3 資料結構
AQS中最基本的資料結構是Node,Node即為CLH變體佇列中的節點。
static final class Node {
// 表示執行緒以共享的模式等待鎖
static final Node SHARED = new Node();
// 表示執行緒正在以獨佔的方式等待鎖
static final Node EXCLUSIVE = null;
// 為1,表示執行緒獲取鎖的請求已經取消了
static final int CANCELLED = 1;
// 為-1,表示執行緒已經準備好了,就等資源釋放了
static final int SIGNAL = -1;
// 為-2,表示節點在等待佇列中,節點執行緒等待喚醒
static final int CONDITION = -2;
// 為-3,當前執行緒處在SHARED情況下,該欄位才會使用
static final int PROPAGATE = -3;
// 當前節點在佇列中的狀態
volatile int waitStatus;
// 前驅節點
volatile Node prev;
// 後續節點
volatile Node next;
// 當前節點的執行緒
volatile Thread thread;
// 指向下一個處於CONDITION狀態的節點