前言
回顧前面:
只有光頭才能變強!
本來我是打算在這章節中寫Lock的子類實現的,但看到了AQS的這麼一個概念,可以說Lock的子類實現都是基於AQS的。
AQS我在面試題中也見過他的身影,但一直不知道是什麼東西。所以本篇我就來講講AQS這個玩意吧,至少知道它的概念是什麼,對吧~
那麼接下來我們就開始吧~
一、AQS是什麼?
首先我們來普及一下juc是什麼:juc其實就是包的縮寫(java.util.concurrnt)
- 不要被人家唬到了,以為juc是什麼一個牛逼的東西。其實指的是包而已~
我們可以發現lock包下有三個抽象的類:
- AbstractOwnableSynchronizer
- AbstractQueuedLongSynchronizer
- AbstractQueuedSynchronizer
通常地:AbstractQueuedSynchronizer簡稱為AQS
我們Lock之類的兩個常見的鎖都是基於它來實現的:
那麼我們來看看AbstractQueuedSynchronizer到底是什麼,看一個類是幹什麼的最快途徑就是看它的頂部註釋
通讀了一遍,可以總結出以下比較關鍵的資訊:
- AQS其實就是一個可以給我們實現鎖的框架
- 內部實現的關鍵是:先進先出的佇列、state狀態
- 定義了內部類ConditionObject
- 擁有兩種執行緒模式
- 獨佔模式
- 共享模式
- 在LOCK包中的相關鎖(常用的有ReentrantLock、 ReadWriteLock)都是基於AQS來構建
- 一般我們叫AQS為同步器
二、簡單看看AQS
上面也提到了AQS裡邊最重要的是狀態和佇列,我們接下來就看看其原始碼是怎麼樣的...
2.1同步狀態
使用volatile修飾實現執行緒可見性:
修改state狀態值時使用CAS演算法來實現:
2.2先進先出佇列
這個佇列被稱為:CLH佇列(三個名字組成),是一個雙向佇列
看看它佇列原始碼的組成:
static final class Node {
// 共享
static final Node SHARED = new Node();
// 獨佔
static final Node EXCLUSIVE = null;
// 執行緒被取消了
static final int CANCELLED = 1;
// 後繼執行緒需要喚醒
static final int SIGNAL = -1;
// 等待condition喚醒
static final int CONDITION = -2;
// 共享式同步狀態獲取將會無條件地傳播下去(沒看懂)
static final int PROPAGATE = -3;
// 初始為0,狀態是上面的幾種
volatile int waitStatus;
// 前置節點
volatile Node prev;
// 後繼節點
volatile Node next;
volatile Thread thread;
Node nextWaiter;
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(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;
}
}
複製程式碼
2.3acquire方法
獲取獨佔鎖的過程就是在acquire定義的,該方法用到了模板設計模式,由子類實現的~
過程:acquire(int)嘗試獲取資源,如果獲取失敗,將執行緒插入等待佇列。插入等待佇列後,acquire(int)並沒有放棄獲取資源,而是根據前置節點狀態狀態判斷是否應該繼續獲取資源,如果前置節點是頭結點,繼續嘗試獲取資源,如果前置節點是SIGNAL狀態,就中斷當前執行緒,否則繼續嘗試獲取資源。直到當前執行緒被park()或者獲取到資源,acquire(int)結束。
來源:
2.4release方法
釋放獨佔鎖的過程就是在acquire定義的,該方法也用到了模板設計模式,由子類實現的~
過程:首先呼叫子類的tryRelease()方法釋放鎖,然後喚醒後繼節點,在喚醒的過程中,需要判斷後繼節點是否滿足情況,如果後繼節點不為且不是作廢狀態,則喚醒這個後繼節點,否則從tail節點向前尋找合適的節點,如果找到,則喚醒.
來源:
三、最後
總結一下AQS到底是什麼吧:
- juc包中很多可阻塞的類都是基於AQS構建的
- AQS可以說是一個給予實現同步鎖、同步器的一個框架,很多實現類都在它的的基礎上構建的
- 在AQS中實現了對等待佇列的預設實現,子類只要重寫部分的程式碼即可實現(大量用到了模板程式碼)
有興趣的同學可去看原始碼和下面的連結繼續學習,我這裡就不講述了。簡簡單單把AQS過一遍~
明天就看Lock顯式鎖實現咯~~~
參考資料:
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。
文章的目錄導航: