簡(jiǎn)介
AQS(AbstractQueuedSynchronizer) 是通過(guò)FIFO隊(duì)列實(shí)現(xiàn)的一套框架,用于阻塞鎖以及同步器比如semaphores代承、countdownlatch现拒、lock的實(shí)現(xiàn)均依賴它里逆。它通過(guò)一個(gè)原子的int值實(shí)現(xiàn)的同步,子類可以通過(guò)保護(hù)方法改變?cè)搃nt變量的值。
它提供了兩種模式顽照,分別為獨(dú)占模式和共享模式,獨(dú)占模式只允許被一個(gè)線程獲取到而共享模式則允許多個(gè)線程獲取。本文主要介紹的幾個(gè)方法就是這兩種模式的體現(xiàn)代兵。
獨(dú)占模式
實(shí)現(xiàn)方式:一個(gè)線程獲取到鎖并釋放后尼酿,后續(xù)線程才能獲取到鎖,在隊(duì)列中表示為當(dāng)頭結(jié)點(diǎn)將鎖釋放后會(huì)將后繼結(jié)點(diǎn)喚醒
- void acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
# tryAcquire 為具體實(shí)現(xiàn)植影,就是對(duì)state變量的操作
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
#前置節(jié)點(diǎn)
final Node p = node.predecessor();
#前置節(jié)點(diǎn)為頭節(jié)點(diǎn)并且資源可以獲取到
if (p == head && tryAcquire(arg)) {
#將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)裳擎,前置節(jié)點(diǎn)不再維護(hù)了
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
#shouldParkAfterFailedAcquire作用就是將前置節(jié)點(diǎn)狀態(tài)置為SIGNAL,下面詳細(xì)分析思币,parkAndCheckInterrupt是將當(dāng)前線程阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
#節(jié)點(diǎn)狀態(tài)為SIGNAL時(shí)返回
return true;
if (ws > 0) {
# 將取消的節(jié)點(diǎn)跳過(guò)
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
#前置節(jié)點(diǎn)狀態(tài)設(shè)置為SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
舉例說(shuō)明鹿响,當(dāng)有兩個(gè)線程A、B調(diào)用tryAcquire方法時(shí)谷饿,A惶我、B按照順序依次入隊(duì)新來(lái)的始終排在隊(duì)尾,只要A還沒(méi)有獲取到博投,那么A肯定是阻塞的绸贡,它的后繼節(jié)點(diǎn)也肯定是阻塞的
畫個(gè)圖表示下:
- boolean 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;
}
#tryRelease為具體實(shí)現(xiàn),也是對(duì)state變量的操作
它的實(shí)現(xiàn)也是比較簡(jiǎn)單就是當(dāng)資源拿到時(shí)毅哗,直接將鏈表的頭部節(jié)點(diǎn)的后繼節(jié)點(diǎn)喚醒听怕,喚醒代碼如下:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
#頭節(jié)點(diǎn)狀態(tài)置為0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
當(dāng)后繼節(jié)點(diǎn)被喚醒后,那么該節(jié)點(diǎn)對(duì)應(yīng)的線程會(huì)繼續(xù)執(zhí)行acquireQueued方法中的for循環(huán)虑绵,這時(shí)當(dāng)拿到資源后尿瞭,那么就會(huì)將該節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn),返回成功了
共享模式
實(shí)現(xiàn)方式:一個(gè)線程獲取到鎖后翅睛,會(huì)通知后繼結(jié)點(diǎn)筷厘,后繼結(jié)點(diǎn)獲取到鎖后繼續(xù)向后傳播,這樣就能使得多個(gè)線程獲取到同一個(gè)鎖
- void acquireShared(int arg)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
# tryAcquire 為具體實(shí)現(xiàn)宏所,就是對(duì)state變量的操作
#與acquireQueued的不同之處在于酥艳,該方法加入的節(jié)點(diǎn)類型為SHARED,另外就是在獲取到資源>0時(shí)爬骤,該方法會(huì)將做傳播操作
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
#r>0當(dāng)前節(jié)點(diǎn)置為頭節(jié)點(diǎn)充石,并傳播,下面細(xì)說(shuō)
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
#當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)
setHead(node);
#當(dāng)獲取資源數(shù)大于0 或者頭部不存在或者之前的以及當(dāng)前的頭節(jié)點(diǎn)狀態(tài)小于0霞玄,則當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)需要被喚醒
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
#后繼節(jié)點(diǎn)為共享類型
if (s == null || s.isShared())
doReleaseShared();
}
#作用就是將頭節(jié)點(diǎn)狀態(tài)從SIGNAL置為0骤铃,并喚醒后繼節(jié)點(diǎn),
#再將頭節(jié)點(diǎn)狀態(tài)置為PROPAGATE坷剧,這樣當(dāng)后繼節(jié)點(diǎn)被喚醒后惰爬,
#它獲取到資源后把自己置為頭結(jié)點(diǎn),繼續(xù)重復(fù)之前動(dòng)作喚醒后繼節(jié)點(diǎn)
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
- boolean releaseShared(int arg)
上邊已經(jīng)介紹
過(guò)程如下圖所示
初始階段有3個(gè)線程都來(lái)調(diào)用acquireShared時(shí)惫企,狀態(tài)如下
當(dāng)A獲取到資源時(shí)撕瞧,則