AQS簡單介紹
AbstractQueuedSynchronizer結(jié)構(gòu)簡單介紹
通過其內(nèi)部結(jié)構(gòu)大致可以了解到
1、AQS其實(shí)是一個雙向鏈表,有head湿痢、tail節(jié)點(diǎn)分別代表首尾節(jié)點(diǎn);每個節(jié)點(diǎn)的元素的類型是Node;
2霍骄、在AQS中維護(hù)了一個int類型的state屬性,這個屬性在不同的子類中存在不同的含義;例如在Semaphore中表示的就是信號量;在ReentrantLock中表示的是當(dāng)前線程獲取鎖的可重入次數(shù);
3台囱、在Node節(jié)點(diǎn)中的thread表示的是進(jìn)入到AQS中的線程;其中SHARED表示的是該線程是在獲取共享資源被阻塞后臺放入到阻塞隊列中的;EXCLUSIVE表示是在獲取獨(dú)占資源被阻塞后放入到阻塞隊列中的;在Node中的waitStatus表示的是對應(yīng)線程的狀態(tài),其中CANCELLED(1:線程已經(jīng)被取消)、SIGNAL(-1:表示線程需要被喚醒)读整、CONDITION(-2:線程在條件隊列里面等待)簿训、PROPAGATE(-3:釋放共享資源時需要通知其他線程)表示對應(yīng)的
大致了解之后,來看一下AQS中幾個相當(dāng)重要的方法;
- void acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
1、該方法獲取獨(dú)占資源,先調(diào)用tryAcquire方法嘗試獲取獨(dú)占資源;該方法的實(shí)現(xiàn)有子類定義;
2绘沉、在獨(dú)占的資源沒有獲取到的時候,向隊列中添加一個EXCLUSIVE類型的的Node節(jié)點(diǎn)到AQS阻塞隊列的尾部;
3煎楣、然后調(diào)用LockSupport.park(this)掛起;
接下來一個簡單了解一下每個方法的內(nèi)部執(zhí)行邏輯
- boolean addWaiter(Node mode)
private Node addWaiter(Node mode) {
//(1)
Node node = new Node(Thread.currentThread(), mode);
//(2)
Node pred = tail;
//(3)
if (pred != null) {
//(3.1)
node.prev = pred;
//(3.2)
if (compareAndSetTail(pred, node)) {
//(3.2.1)
pred.next = node;
//(3.2.2)
return node;
}
}
//(4)
enq(node);
return node;
}
(1)為當(dāng)前線程構(gòu)建一個新的node節(jié)點(diǎn);
(2)獲取AQS隊列中的尾節(jié)點(diǎn);
(3)判斷尾節(jié)點(diǎn)是否不為null;
(3.1)將新的節(jié)點(diǎn)的前置節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn);
(3.2)通過CAS將當(dāng)前的尾節(jié)點(diǎn)設(shè)置為新的節(jié)點(diǎn);這里使用CAS的原因是為了防止其他的線程將AQS隊列中的tail元素修改成了別的Node元素;
(3.2.1)CAS操作成功之后將將尾節(jié)點(diǎn)的后置節(jié)點(diǎn)設(shè)置為新的節(jié)點(diǎn);操作失敗進(jìn)入(4)
(3.2.2)返回新節(jié)點(diǎn)
(4)當(dāng)前AQS隊列中的tail節(jié)點(diǎn)為null,那么這個時候可以認(rèn)為AQS隊列為null,然后通過enq(fianl Node node)方法進(jìn)行元素新增;
接下來簡單分析一下enq(fianl Node node)方法;
- Node enq(final Node node)
private Node enq(final Node node) {
for (;;) {
//(1)
Node t = tail;
//(2)
if (t == null) { // Must initialize
//(2.1)
if (compareAndSetHead(new Node()))
//(2.2)
tail = head;
//(3)
} else {
//(3.1)
node.prev = t;
//(3.2)
if (compareAndSetTail(t, node)) {
//(3.3)
t.next = node;
return t;
}
}
}
}
此方法根據(jù)boolean addWaiter(Node mode)方法的執(zhí)行情況進(jìn)行分析;
1豺总、當(dāng)AQS中的tail節(jié)點(diǎn)為null時,進(jìn)入此方法,也就是第一次循環(huán)
(1)這里重新做一次取值,是為了避免在此過程中,tail被其他線程修改了;這里默認(rèn)沒有被修改,那么在(2)中t== null成立;
(2.1)中將head節(jié)點(diǎn)設(shè)置為哨兵節(jié)點(diǎn)(new Node());
(2.2)head節(jié)點(diǎn)設(shè)置成功之后,將head節(jié)點(diǎn)賦值tail節(jié)點(diǎn);這個熟tail節(jié)點(diǎn)就不為空了;
2车伞、(2.2)中將head節(jié)點(diǎn)的值賦值給了tail節(jié)點(diǎn)之后,第一次循環(huán)結(jié)束,進(jìn)行第二次循環(huán),這次循環(huán)很明顯tail != null了
然后進(jìn)入(3)
(3.1)將新增的節(jié)點(diǎn)的前置節(jié)點(diǎn)只想哨兵節(jié)點(diǎn)
(3.2)設(shè)置尾節(jié)點(diǎn)tail尾新增的節(jié)點(diǎn)node;
(3.3)將哨兵節(jié)點(diǎn)的后置節(jié)點(diǎn)指向新增的節(jié)點(diǎn)node,然后返回
執(zhí)行相關(guān)的大致流程圖:
然后再看acquireQueued(final Node node, int arg)方法
- boolean acquireQueued(final Node node, int arg)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//(1)
final Node p = node.predecessor();
//(2)
if (p == head && tryAcquire(arg)) {
//(2.1)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
接著addWaiter方法來分析一下acquireQueued方法;
1、 獲取當(dāng)前節(jié)點(diǎn)的prev節(jié)點(diǎn)
2喻喳、 如果prev節(jié)點(diǎn)為head節(jié)點(diǎn)另玖,那么它就有資格去爭搶鎖,調(diào)用tryAcquire搶占鎖
3表伦、 搶占鎖成功以后谦去,把獲得鎖的節(jié)點(diǎn)設(shè)置為head,并且移除原來的初始化head節(jié)點(diǎn)
4蹦哼、如果獲得鎖失敗鳄哭,則根據(jù)waitStatus決定是否需要掛起線程
5、 最后纲熏,通過cancelAcquire取消獲得鎖的操作
2妆丘、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;
}
1锄俄、該方法會先調(diào)用boolean tryRelease(int arg)方法嘗試釋放資源(具體實(shí)現(xiàn)由子類定義)
2、在釋放資源成功后,判斷AQS隊列中是否存在被阻塞的線程
3勺拣、存在被阻塞的線程則調(diào)用void unparkSuccessor(Node node)進(jìn)行喚醒;