AQS中一定要記住2點:
1.處理流程:
if(!請求成功)
加入隊列
2.請求是對state的判斷,AQS不關心你state表示什麼,你可以表示狀态也可以表示數量,由子類實作對請求的判斷。将規則的判斷和規則的處理分離,有點像模闆模式。
先想想什麼是獨占什麼是共享,舉個栗子:獨占就像大家拿号去排隊體檢,你拿号了發現前面還有n個人,沒辦法,等吧,然後你前面的人體檢完了,醫生就說,你通知下一位吧,ok,你出來通知排你後面的人,這個人有可能是跟占座位似得就放在紙在哪,是以你跳過他,再通知後面真正有人的進去。而共享則不同了,這個号可能不止屬于你一個人,可能屬于你公司所有體檢的人,是以拿号排隊輪到你的時候,你就需要通知排隊的所有同僚,大家一起體檢去啊(這個共享的不太恰當,應該當成condition例子來說才好)。感覺獨占和共享的大概意思就是這樣。
還是看下AQS的共享模式
public final void acquireShared(int arg) {
//tryAcquireShared請求判斷又是由子類實作判斷
if (tryAcquireShared(arg) < 0)
//失敗後加入隊列
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
//節點狀态是共享,之前的獨占模式是EXCLUSIVE
//跟獨占一樣加入隊列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//還是判斷pre是不是頭結點,是就再次請求
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
//這裡和獨占不同,獨占模式下隻是設定成頭結點
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);
}
}
發現共享整個挂起的doAcquireShared方法跟獨占模式的acquireQueued處理差不多,唯一有差別的似乎就是如果pre節點是頭結點,目前節點請求成功,獨占模式隻是将節點設定為head然後return,這裡除了sethead還将喚醒隊列的中其他節點,其他方法不管,繼續看setHeadAndPropagate:
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
//目前node請求成功後判斷next節點是共享節點就繼續release
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
/*
使用for保證隊列中節點一定會被傳遞,即使有其他acquire或release在進行
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//節點狀态為SIGNAL表示需要向後傳遞
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // 失敗了就loop
unparkSuccessor(h);
}
//為0就設定成PROPAGETE表示需要傳播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
這裡最重要的是了解doReleaseShared,一個程序 doReleaseShared,然後unparkSuccessor,這時候被喚醒的其他線程可能線程切換運作,重新請求修改head節點,沒機會的話,這裡檢查head節點沒有變化就繼續for,一直等到被喚醒的線程時間片切換到,然後再将繼續修改下一個節點,到最後隊列中的所有節點都被喚醒。
這裡一定要想着線程切換多看看幾遍,我當時就郁悶了半天這個疑問。
對應的acquireSharedInterruptibly響應中斷和tryAcquireSharedNanos響應中斷逾時,跟獨占的都差不多。
共享模式release,這個沒什麼好說的,釋放判斷成功就doReleaseShared,把隊列中所有節點release
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
共享跟獨占的流程圖差不多,不想畫了,改天學習下AQS的condition。
參考:
http://www.infoq.com/cn/articles/java8-abstractqueuedsynchronizer