[TOC]
抽象隊列同步器AbstractQueuedSynchronizer (以下都簡稱AQS),是用來構建鎖或者其他同步組件的基礎框架,它具備兩個核心功能:
* 使用了一個int成員變量來表示同步狀態
* 通過內置的隱式FIFO同步隊列(first-in-first-out)來控制獲取共享資源的線程們
## 核心方法
### 獨占式獲取與釋放同步狀態
方法描述void acquire(int arg)獨占式獲取同步狀態(該方法會調用子類重寫的tryAcquire(int arg))。 如果當前線程獲取同步狀態成功,則返回以便繼續執行,否則進入同步隊列的尾部等待,該方法會調用tryAcquire(int arg)方法。void acquireInterruptibly(int arg)與 void acquire(int arg)基本邏輯相同,但是該方法響應中斷。 如果當前沒有獲取到同步狀態,那么就會進入等待隊列,如果當前線程被中斷(Thread().interrupt()),那么該方法將會拋出InterruptedException。并返回boolean tryAcquireNanos(int arg, long nanosTimeout)在acquireInterruptibly(int arg)的基礎上,增加了超時限制,如果在超時時間內獲取到同步狀態返回true,否則返回falseboolean release(int arg)獨占式釋放同步狀態,該方法會在釋放同步狀態后將第一個節點(對應剛剛釋放同步狀態的線程)的后繼節點對應的線程喚醒。
### 共享式獲取與釋放同步狀態
方法描述void acquireShared(int arg)共享式的獲取同步狀態(該方法會調用子類重寫的tryAcquireShared(int arg)). 如果當前線程未獲取到同步狀態,將會進入同步隊列的尾部等待,與獨占式獲取的主要區別是在同一時刻可以有多個線程獲取到同步狀態。void acquireSharedInterruptibly(int arg)在acquireShared(int arg)的基本邏輯相同,增加了響應中斷。boolean tryAcquireSharedNanos(int arg, long nanosTimeout)在acquireSharedInterruptibly的基礎上,增加了超時限制。boolean releaseShared(int arg)共享式的釋放同步狀態
## 同步隊列
同步隊列是AQS很重要的組成部分,它是一個雙端隊列,遵循FIFO原則,主要作用是用來存放在鎖上阻塞的線程,當一個線程嘗試獲取鎖時,如果已經被占用,那么當前線程就會被構造成一個Node節點假如到同步隊列的尾部,隊列的頭節點是成功獲取鎖的節點,當頭節點線程是否鎖時,會喚醒后面的節點并釋放當前頭節點的引用。?//得到鎖的在頭部,得不到的在尾部。
~~~
static final class Node {
//等待狀態
volatile int waitStatus;
//當前轉換為Node節點的線程。
volatile Thread thread;
//當前節點在同步隊列中的上一個節點。
volatile Node prev;
//當前節點在同步隊列中的下一個節點。
volatile Node next;
//Node既可以作為同步隊列節點(競爭使用共享資源的線程)使用,也可以作為Condition的等待隊列節點(類似與調用Obect.wait等待線程)使用(將會在后面講Condition時講到)。
//在作為同步隊列節點時,nextWaiter可能有兩個值:EXCLUSIVE、SHARED標識當前節點是獨占模式還是共享模式;
//在作為等待隊列節點使用時,nextWaiter保存后繼節點。
Node nextWaiter;
}
~~~
## 等待狀態主要包含以下狀態
狀態值含義CANCELLED1被中斷或獲取同步狀態超時的線程將會被置為該狀態,且該狀態下的線程不會再阻塞。SIGNAL-1線程的后繼線程正/已被阻塞,當該線程release或cancel時要重新這個后繼線程(unpark)CONDITION-2標識當前節點是作為等待隊列節點使用的。當前節點在Condition中的等待隊列上,(關于Condition會在下篇文章進行介紹),其他線程調用了Condition的singal()方法后,該節點會從等待隊列轉移到AQS的同步隊列中,等待獲取同步鎖。PROPAGATE-3與共享式獲取同步狀態有關,該狀態標識的節點對應線程處于可運行的狀態。00初始狀態
其中, SIGNAL是個很重要的概念:
1. 當前節點的線程如果釋放了或取消了同步狀態,將會將當前節點的狀態標志位SINGAL,用于通知當前節點的下一節點,準備獲取同步狀態。
2. 并且每個節點在阻塞前,需要標記其前驅節點的狀態為SIGNAL。

~~~
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private transient volatile Node head;
private transient volatile Node tail;
//保證了添加頭節點 和 尾節點 加入的過程必須保證線程安全
private final boolean compareAndSetTail(Node expect, Node update) {
return U.compareAndSwapObject(this, TAIL, expect, update);
}
}
~~~
## 獨占式同步狀態獲取
* **addWaiter構建節點并加入隊尾**
> 1\. 如果當前尾指針(tail)不為null,那么嘗試將尾指針 tail 指向當前線程構造的Node節點,如果成功,那么將尾指針之前指向的節點的next指向當前線程構造的Node節點,并返回當前節點。
> 2\. 如果當前尾節點為空,即當前同步隊列為空,則新建一個傀儡節點作為首節點和尾節點,然后再將當前節點設為尾節點。
* **acquireQueued 自旋式競爭或阻塞當前線程并重置pre關系**
> 1\. 把已經追加到隊列的線程節點(addWaiter方法返回值)進行阻塞,但阻塞前又通過tryAccquire重試是否能獲得鎖,如果重試成功能則無需阻塞,直接返回
> 2\. \*\* 再次被喚醒時,繼續循環體,相當于 自旋式競爭鎖\*\*
* **cancelAcquire取消并移除當前節點和喚醒下一個節點**
> 如果線程中斷了,那么就將該線程從同步隊列中移除,同時喚醒下一節點
**阻塞最終是調用 LockSupport.park(this);堵塞進程**
## 獨占式同步狀態釋放
線程獲取同步狀態成功并執行相應邏輯后,需要釋放同步狀態,使得后繼線程節點能夠繼續獲取同步狀態,通過調用AQS的relase(int arg)方法,可以釋放同步狀態。
boolean release(int arg)獨占式釋放同步狀態,該方法會在釋放同步狀態后將第一個節點(對應剛剛釋放同步狀態的線程)的后繼節點對應的線程喚醒。
- Java
- Object
- 內部類
- 異常
- 注解
- 反射
- 靜態代理與動態代理
- 泛型
- 繼承
- JVM
- ClassLoader
- String
- 數據結構
- Java集合類
- ArrayList
- LinkedList
- HashSet
- TreeSet
- HashMap
- TreeMap
- HashTable
- 并發集合類
- Collections
- CopyOnWriteArrayList
- ConcurrentHashMap
- Android集合類
- SparseArray
- ArrayMap
- 算法
- 排序
- 常用算法
- LeetCode
- 二叉樹遍歷
- 劍指
- 數據結構、算法和數據操作
- 高質量的代碼
- 解決問題的思路
- 優化時間和空間效率
- 面試中的各項能力
- 算法心得
- 并發
- Thread
- 鎖
- java內存模型
- CAS
- 原子類Atomic
- volatile
- synchronized
- Object.wait-notify
- Lock
- Lock之AQS
- Lock子類
- 鎖小結
- 堵塞隊列
- 生產者消費者模型
- 線程池