高並發編程-AQS深入解析
要點解說
AbstractQueuedSynchronizer簡稱AQS,它是java.util.concurrent包下CountDownLatch/FutureTask/ReentrantLock/RenntrantReadWriteLock/Semaphore實現的基礎,所以深入理解AQS非常有必要。
AQS通過內部實現的FIFO等待隊列來完成資源獲取線程的等待工作,如果當前線程獲取資源失敗,AQS則會將當前線程以及等待狀態等信息構造成一個Node結構的節點,並將其加入等待隊列中,同時會阻塞當前線程;當其它獲取到資源的線程釋放持有的資源時,則會把等待隊列節點中的線程喚醒,使其再次嘗試獲取對應資源。
源碼解析
AbstractQueuedSynchronizer源碼比較長,這裡只分析主要的功能代碼。首先,先看一下它內部定義的Node類的代碼。
static final class Node { //聲明共享模式下的等待節點 static final Node SHARED = new Node(); //聲明獨佔模式下的等待節點 static final Node EXCLUSIVE = null; //waitStatus的一常量值,表示線程已取消 static final int CANCELLED = 1; //waitStatus的一常量值,表示後繼線程需要取消掛起 static final int SIGNAL = -1; //waitStatus的一常量值,表示線程正在等待條件 static final int CONDITION = -2; //waitStatus的一常量值,表示下一個acquireShared應無條件傳播 static final int PROPAGATE = -3; //waitStatus,其值只能為CANCELLED、SIGNAL、CONDITION、PROPAGATE或0 //初始值為0 volatile int waitStatus; //前驅節點 volatile Node prev; //後繼節點 volatile Node next; //當前節點的線程,在節點初始化時賦值,使用後為null volatile Thread thread; //下一個等待節點 Node nextWaiter; Node() { } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
上面的Node就是等待隊列里的一個節點,具體結構如下:
接著,來看一下AbstractQueuedSynchronizer的三個重要屬性:
//等待隊列的頭結點 private transient volatile Node head; //等待隊列的尾節點 private transient volatile Node tail; //同步狀態,這個很重要 private volatile int state;
從這就可以得到同步隊列的基本結構:
protected final int getState() { return state; } protected final void setState(int newState) { state = newState; } //使用CAS設置同步狀態,確保線程安全 protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
AbstractQueuedSynchronizer類中其它方法主要是用於插入節點、釋放節點,插入節點過程如下圖所示:
釋放頭結點過程如下圖所示:
分析小結
AbstractQueuedSynchronizer實現了對資源獲取與釋放的基礎實現,真正使用到的地方還在是各個具體的功能類中,如CountDownLatch、ReentrantLock等,後面在這些類中會具體分析。
面試考點
AQS是什麼?內部實現結構了解嗎?
AbstractQueuedSynchronizer簡稱AQS,它為實現依賴於先進先出 (FIFO) 等待隊列的阻塞鎖和相關同步器(信號量等)提供一個基礎實現框架。內部實現結構參考上面的圖示作答。推薦閱讀:
※golang簡單key/value資料庫(二)
※高並發和高性能系統中進程、線程、協程、隊列的詳解,以及各運行模式的對比
※Python操作rabbitmq系列(三):多個接收端消費消息
※性能測試筆記(一):吞吐量與並發數
※Go並發調度器解析之實現一個協程池