AQS(Abstracting Queue Sychronizer),望文生義,即一個抽象隊列 + 一個須要同步的狀態,所謂抽象隊列即這個隊列並非真是存在的(通俗的講,不是一個LinkedList對象),而是像HashMap中的鏈表同樣,只存在Node之間的關係中,每一個Node負責維護前置與後置節點,以及持有一個請求線程(能夠理解爲將一個請求線程封裝成Node);node
串行化
,不知道在看的你看沒看過 《高性能Mysql》 ,其中就提到一個郵箱系統,其實與這個場景相似,在事務隔離級別爲第四級串行化時是能夠保證線程安全的;/**
* The synchronization state.
*/
private volatile int state;
複製代碼
這個值當你本身去實現鎖的時候你能夠本身定義規則, 《Java併發編程的藝術》 一書中本身定義了一個能夠同時被兩個線程持有的鎖(共享式),而且將state值設置爲2,每當有一個線程獲取到鎖後,將該值減1,當state值再減去一便小於零時,這個線程便只能加入同步隊列而且開始自旋等待鎖。sql
LockSupport().park()方法將Node中的線程狀態改成WAITING,等待被喚醒或被中斷
);wait()方法
,該線程所持有的鎖會被釋放並將該線程加入等待隊列中,而Condition是調用
await()方法
將該線程放入對應的Condition所持有的等待隊列中去(我以爲能夠把Condition理解成操做系統中定義的線程喚醒條件),因此有幾個Condition就會有幾個對應的等待隊列;
acquireQueue()方法中是這樣定義判斷條件的
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//獲取當前節點的前置節點
final Node p = node.predecessor();
//注意這個與判斷條件,第一個就是當前節點的前置節點是不是頭節點,而做爲第一個判斷條件是由於與判斷有一個爲非即爲非,就不會進行第二個條件的判斷,這個在之後的編程中也是值得學習的
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
複製代碼
這樣也就保證了獲取共享資源的順序性(即按照插入到隊列的時間來定)編程
getState():獲取當前同步狀態
setState():設置當前同步狀態
compareAndSetState(int expect, int update):使用CAS設置當前狀態,該方法可以保證狀態設置的原子性
這裏AQS的設計者採用了模板設計模式
將對同步狀態的操做定義好過程,而將其中能夠改變的過程交由每一個具體的同步器(即鎖)來實現,保證了每一個同步器的特殊性; 上面講的可能有點籠統,那咱們不妨分析一下AQS定義的模板是什麼?但在此以前,咱們必定要牢記於心的是AQS是一個同步框架,即它全部的操做都是爲了保證共享變量的安全!設計模式
acquire()方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
複製代碼
能夠看到這個方法會調用tryAcquire()方法 和咱們以前提到的 acquireQueued()方法
,前者就是一個須要子類實現的模板方法,爲何必定要子類去實現,由於每種鎖都應該本身去定義當前共享變量處在一個什麼狀態下時,請求線程能夠得到共享資源的訪問權(舉個例子,獨佔鎖時,只要當前共享資源有線程在訪問,那麼以後全部請求線程都不能夠再獲取到鎖;而若是是共享鎖,那麼這個方法就要再共享資源狀態可訪問數容許的狀況下讓該請求線程獲取到鎖);而若是子類定義的tryAcquire() 認爲當前線程獲取不到鎖,就應該調用acquireQueued() 方法去死循環+CAS嘗試獲取鎖安全
release(int arg)
方法public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
複製代碼
同acquire()同樣,如何去判斷是否能夠釋放對共享資源的訪問權也是須要不一樣的鎖本身去經過覆蓋AQS中的tryRelease()方法
去本身定義;bash
FairSync.lock()方法
//直接調用AQS的acquire()方法,這個輸入參數在此處沒有意義)
final void lock() {
acquire(1);
}
複製代碼
而這個acquire()方法利用Java多態實則是調用了FairSync的tryAcquires()方法多線程
/**
* Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires){ //省略了具體邏輯 } } 複製代碼
ReentrantLock() 可讓線程在等待時間過長時放棄等待轉而去作其餘事情
,那若是此時這個線程在同步隊列上這麼辦?因此咱們須要將線程的當前狀態信息同步到Node節點中去;InterruptedException
,並設置線程的中斷標誌位;public class InterruptRunnableDemo extends Thread{
@Override
public void run() {
//while循環的條件是當前線程的中斷標誌位是否爲True
while (!Thread.currentThread().isInterrupted()) {
System.out.println("running");
}
System.out.println("done ");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new InterruptRunnableDemo();
thread.start();
Thread.sleep(1000);
//打斷子線程,並設置線程的中斷標誌位
thread.interrupt();
}
}
複製代碼
輸出結果爲:
running
running
running
done
複製代碼
參考文章: zhuanlan.zhihu.com/p/27857336併發
waitStatus : (1) CANCELLED :因爲在同步隊列中等待的線程等待超時或被中斷,須要從同步隊列中取消等待,節點進入該狀態將不會變化;(2)SIGNAL:當前持有同步狀態的節點釋放或被取消時,在這個狀態下,會通知後繼處於等待狀態的節點,使得後繼節點的線程得以運行 (3)CONDITION:節點在等待隊列中時節點處於此狀態 (4)PROPAGATE :表示下一次共享式同步狀態將會無條件被傳播下去 (5)INITAL: 初始化狀態
此時咱們須要關注的即是CANCELLED狀態,節點是如何從其餘狀態變爲CANCELLED狀態的,而且進入這個狀態對於維護等待隊列有什麼幫助?acquireInterruptibly(int arg)
方法,這個方法與acquire()方法相同
,可是該方法響應中斷,而且當前線程未獲取到同步狀態而進入同步隊列中時,若是這個線程被中斷,那麼該方法會拋出InterruptedException
並返回; 而acquire()方法是當節點經過tryAcquire()方法成功拿到訪問共享資源的權力時,再去校驗當前線程的中斷標誌位,若是爲True則將Node的waitStatus狀態改成CANCELLED,而且seltInterrupt()public final void acquireInterruptibly(int arg)
throws InterruptedException {
//調用時也會判斷此時這個線程的中斷標誌位是否以及爲True,是則直接拋出異常,並讓調用者進行處理
if (Thread.interrupted())
throw new InterruptedException();
//若是此時該節點已經獲取到鎖,但若是這個節點中的線程的中斷標誌位爲True則也會拋出異常,而後調用doAcquireInterruptibly()中的finally代碼塊中的cancelAcquire()方法,將waitStatus狀態改成CANCELLED
if (!tryAcquire(arg)
doAcquireInterruptibly(arg);
}
複製代碼
unparkSuccessor(Node)方法
,這個方法會將全部狀態位CANCELLED
的Node設置位null釋放掉,不會再影響其後活躍線程競爭共享資源的訪問權!