AQS是什么?
AQS 全稱是 AbstractQueuedSynchronizer, 它提供一種依賴于FIFO等待隊(duì)列的構(gòu)建鎖和同步器的框架。
CAS是什么?
CAS(Compare And Swap),即比較并交換桅锄。是解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制,CAS操作包含三個(gè)操作數(shù)——內(nèi)存位置(V)样眠、預(yù)期原值(A)和新值(B)友瘤。如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值檐束。否則辫秧,處理器不做任何操作。無(wú)論哪種情況被丧,它都會(huì)在CAS指令之前返回該位置的值具则。CAS有效地說(shuō)明了“我認(rèn)為位置V應(yīng)該包含值A(chǔ)麻裳;如果包含該值铭腕,則將B放到這個(gè)位置券时;否則,不要更改該位置黄选,只告訴我這個(gè)位置現(xiàn)在的值即可蝇摸。
底層實(shí)現(xiàn)
整體結(jié)構(gòu)
-
AQS 的結(jié)構(gòu)大概可總結(jié)為以下 3 部分:
- 用 volatile 修飾的整數(shù)類型的 state 狀態(tài),用于表示同步狀態(tài)办陷,提供 getState 和 setState, compareAndSetState來(lái)操作同步狀態(tài)貌夕;
- 提供了一個(gè) FIFO 等待隊(duì)列,實(shí)現(xiàn)線程間的競(jìng)爭(zhēng)和等待民镜,這是 AQS 的核心啡专;其中, 鏈表頭Head和鏈表尾Tail也有volatile修飾。
- AQS 內(nèi)部提供了各種基于 CAS 原子操作方法制圈,如 compareAndSetState 方法植旧,并且提供了鎖操作的acquire和release方法辱揭。
-
提供兩種鎖的默認(rèn)實(shí)現(xiàn)方式:
- 獨(dú)占鎖(Exclusive)
- 共享鎖(Shared)
tryAcquire, tryRelease 都是需要實(shí)現(xiàn)類自己去實(shí)現(xiàn)的方法, 如果不實(shí)現(xiàn)的話,是會(huì)拋出異常UnsupportedOperationException的
用到的設(shè)計(jì)模式-模板模式, 在模板模式(Template Pattern)中病附,一個(gè)抽象類公開定義了執(zhí)行它的方法的方式/模板。它的子類可以按需要重寫方法實(shí)現(xiàn)亥鬓,但調(diào)用將以抽象類中定義的方式進(jìn)行完沪。這種類型的設(shè)計(jì)模式屬于行為型模式。
獨(dú)占鎖
acquire獲取獨(dú)占鎖
- 偽代碼實(shí)現(xiàn)
while (!tryAcquire(arg)) {
<em>enqueue thread if it is not already queued</em>;
<em>possibly block current thread</em>;
}
- 代碼實(shí)現(xiàn)
- 先嘗試獲取鎖嵌戈,獲取成功則成功
- 嘗試失敗覆积,則把當(dāng)前線程包裝成為一個(gè)節(jié)點(diǎn),然后等待獲取的機(jī)會(huì)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- NOTE這里有必要說(shuō)明一下熟呛,就是當(dāng)一個(gè)節(jié)點(diǎn)成為head節(jié)點(diǎn)的時(shí)候宽档,他不一定會(huì)是下一個(gè)獲取鎖的節(jié)點(diǎn),從上面代碼也可以看出來(lái)庵朝,所以獲取鎖的線程都會(huì)先嘗試獲取鎖一次吗冤,這樣有可能等待隊(duì)列的頭節(jié)點(diǎn)也可能獲取鎖失敗。
release釋放獨(dú)占鎖
- 偽代碼實(shí)現(xiàn)
if (tryRelease(arg))
<em>unblock the first queued thread</em>;
- 代碼實(shí)現(xiàn)
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享鎖
acquireShared獲取共享鎖
- 代碼實(shí)現(xiàn)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
releaseShared釋放共享鎖
- 代碼實(shí)現(xiàn)
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
FIFO隊(duì)列
隊(duì)列模型
這個(gè)在AQS的注釋說(shuō)明里邊九府,Doug Lea已經(jīng)說(shuō)的很明確了椎瘟,還做了圖解
- 這個(gè)等待隊(duì)列是CLH(Craig, Landin, and Hagersten)隊(duì)列的一個(gè)變種,這種隊(duì)列常被用作自旋鎖(這個(gè)概念就不展開了)
- 作用:用于阻塞同步器
- 結(jié)構(gòu)
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
- 入隊(duì):插入隊(duì)尾
- 出隊(duì):直接設(shè)置head即可
節(jié)點(diǎn)
- 節(jié)點(diǎn)其實(shí)是把想要獲取鎖的線程包裝了一番
//mode分exclusive和shared兩種模式
new Node(Thread.currentThread(), mode);
- 節(jié)點(diǎn)狀態(tài)
/** 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;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate.
* 這個(gè)狀態(tài)只在共享鎖的模式下有效侄旬,這個(gè)傳播的用處在哪兒呢肺蔚?
* 舉例說(shuō)明:讀寫鎖,寫讀操作和寫寫操作互斥儡羔,讀讀之間不互斥宣羊;當(dāng)調(diào)用acquireShared獲取讀
* 鎖時(shí),會(huì)檢查后續(xù)節(jié)點(diǎn)是否是獲取讀鎖汰蜘,如果是仇冯,則同樣釋放;
*/
static final int PROPAGATE = -3;
常見面試
- 談一下AQS吧 @可以從定義入手鉴扫,然后講
- 不同鎖狀態(tài)的更改的實(shí)現(xiàn)方式
- FIFO隊(duì)列的實(shí)現(xiàn)方式
- 核心技術(shù)CAS+volatile
- CAS是什么赞枕?見上面的筆記
- 為什么你說(shuō)AQS的底層是CAS+volatile?
- 表示鎖狀態(tài)的變量state坪创,以及FIFO隊(duì)列的頭炕婶,尾,節(jié)點(diǎn)的狀態(tài)都是volatile修飾的
- 在設(shè)置state莱预,隊(duì)列的頭柠掂,尾,狀態(tài)的時(shí)候都有用到CAS技術(shù)
- JUC包里的同步組件主要實(shí)現(xiàn)了AQS的哪些主要方法 依沮?
- tryAcquire, tryRelease
- tryAcquireShared, tryReleaseShared
- isHeldExclusively