AQS是什麼?
在 Lock 中,用到了一個同步隊列 AQS,全稱 AbstractQueuedSynchronizer,它是一個同步工具, 也是 Lock 用來實現線程同步的核心組件, 如果你搞懂了 AQS,那麼 J.U.C 中絕大部分的工具都能輕鬆掌握;
此篇博客所有源碼均來自JDK 1.8
AQS的內部實現
AQS 隊列內部維護的是一個 FIFO 的雙向鏈表,這種結構的特點是每個數據結構都有兩個指針,分別指向直接的後繼節點和直接前驅節點。所以雙向鏈表可以從任意一個節點開始很方便的訪問前驅和後繼。每個 Node 其實是由一個線程封裝,當線程爭搶鎖失敗後會封裝成 Node 加入到 ASQ 隊列中去;當獲取鎖的線程釋放鎖以後,會從隊列中喚醒一個阻塞的節點(阻塞的線程)。
AQS中連個重要的成員變量
private transient volatile Node head; // 頭節點
private transient volatile Node tail; // 尾節點
Node的內部實現
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
// 共享模式時
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
// 獨佔模式時
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1; //節點從同步隊列中取消
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1; //後繼節點的線程處於等待狀態,
如果當前節點釋放同步狀態會通知後繼節點,
使得後繼節點的線程能夠運行; (只有處於singal狀態的節點才能被喚醒)
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3; // 表示下一次共享式同步狀態獲取將會無條件傳播下去
volatile int waitStatus; //節點狀態
volatile Node prev;
volatile Node next;
volatile Thread thread; //加入同步隊列的線程引用
Node nextWaiter; // condition隊列中的後繼節點
// 是否爲共享鎖
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 返回前驅節點
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
// 將線程構造成一個node節點,添加到等待隊列(condition隊列)
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;
}
}
AQS的兩種功能?
獨佔鎖和共享鎖
獨佔鎖:
每次只能有一個線程持有鎖,比如ReentrantLock就是以獨佔方式實現的互斥鎖;
幾個重要的方法:
void acquire(int arg):獨佔式獲取同步狀態,如果獲取失敗則插入同步隊列進行等待;
void acquireInterruptibly(int arg):與acquire方法相同,但在同步隊列中進行等待的時候可以檢測中斷;
boolean tryAcquireNanos(int arg, long nanosTimeout):在acquireInterruptibly基礎上增加了超時等待功能,在超時時間內沒有獲得同步狀態返回false;
boolean release(int arg):釋放同步狀態,該方法會喚醒在同步隊列中的下一個節點
入口:
acquire(int arg) 方法爲AQS提供的模板方法,該方法爲獨佔式獲取同步狀態,但是該方法對中斷不敏感,也就是說由於線程獲取同步狀態失敗加入到CLH同步隊列中,後續對線程進行中斷操作時,線程不會從同步隊列中移除。代碼如下:
// 獨佔鎖入口
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- tryAcquire:去嘗試獲取鎖,獲取成功則設置鎖狀態並返回true,否則返回false。該方法自定義同步組件自己實現,該方法必須要保證線程安全的獲取同步狀態。
- addWaiter:如果tryAcquire返回FALSE(獲取同步狀態失敗),則調用該方法將當前線程加入到CLH同步隊列尾部。
- acquireQueued:當前線程會根據公平性原則來進行阻塞等待(自旋),直到獲取鎖爲止;並且返回當前線程在等待過程中有沒有中斷過。
- selfInterrupt:產生一箇中斷。
共享鎖:
允許多個線程同時獲取鎖,併發訪問共享資源,例如:ReentrantReadWriteLock,CountDownLatch;
幾個重要的方法:
void acquireShared(int arg):共享式獲取同步狀態,與獨佔式的區別在於同一時刻有多個線程獲取同步狀態;
void acquireSharedInterruptibly(int arg):在acquireShared方法基礎上增加了能響應中斷的功能;
boolean tryAcquireSharedNanos(int arg, long nanosTimeout):在acquireSharedInterruptibly基礎上增加了超時等待的功能; boolean releaseShared(int arg):共享式釋放同步狀態
入口:
// 共享鎖入口
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) //獲取失敗,自旋獲取同步狀態
doAcquireShared(arg);
}
tryAcquireShared(int arg)方法嘗試獲取同步狀態,返回值爲int,當其 >= 0 時,表示能夠獲取到同步狀態,這個時候就可以從自旋過程中退出。
如果獲取失敗則調用doAcquireShared(int arg)自旋方式獲取同步狀態,共享式獲取同步狀態的標誌是返回 >= 0 的值表示獲取成功;
acquireShared(int arg)方法不響應中斷,與獨佔式相似,AQS也提供了響應中斷、超時的方法,分別是:acquireSharedInterruptibly(int arg)、tryAcquireSharedNanos(int arg,long nanos),這裏就不做解釋了。
總結
AQS的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態,後續會詳細分析子類,ReentrantLock以及ReentrantReadWriteLock的具體實現。