在上節中解析了AbstractQueuedSynchronizer(AQS)中獨占模式對同步狀態獲取和釋放的實現過程。本節將會對共享模式的同步狀態獲取和釋放過程做一個解析。上一節提到了獨占模式和共享模式的區別,最主要的區別就是在同一時刻能否有多個線程同時獲取到同步狀態。
1).共享模式同步狀態的獲取
這個方法同獨占模式獲取同步狀態的acquire方法一樣,同樣也是一個模板方法,我們簡要回顧一下獨占模式下獲取同步狀態的acquire方法:
//AbstractQueuedSynchronizer#acquire public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //獲取鎖(tryAcquire)->構造節點(addWaiter)->加入隊列(addWaiter)->自旋獲取鎖(acquireQueued) selfInterrupt(); //中斷當前線程 }
//AbstractQueuedSynchronizer#acquireShared public final void acquireShared(int arg) { if (tryAcquireShared(arg)) //獲取鎖,由子類具體實現 doAcquireShared(arg); } 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) { setHeadAndPropagate(node, r); p.next = null; if (inerrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(arg); } }
共享模式下獲取同步狀態的自旋過程和獨占模式大致相同,從代碼實現角度來看不同的是共享模式下把線程構造節點加入隊列,以及在獲取同步狀態后中斷當前線程都放到了同一個方法里doAcquireShared。共享模式同樣也是只有在是其前驅節點是頭結點的時候才會嘗試獲取同步狀態,調用tryAcquireShared獲取同步狀態成功后會返回大於等於0的數,這個時候將會執行setHeadAndPropagate方法,這個方法首先會將當前獲取同步狀態的這個線程置為頭節點(同獨占模式一樣),但在將當前線程置為頭節點過后,又做了一部分操作,其代碼如下:
private void setHeadAndPropagate(Node node, int propagate) { Node h = head; setHead(node); //將當前獲取到同步狀態的線程節點置為頭節點 if (propagate > 0 || h == null || h.waitStatus < 0) { //喚醒后繼節點 Node s = node.next; if (s == null || s.isShared()) doReleaseShared(); //喚醒后繼節點,因為是共享模式,所以允許多個線程同時獲取同步狀態 } }
在doReleaseShared方法中,首先便利隊列中的所有節點,如果節點狀態為SIGNAL,則把SIGNAL狀態置為0(初始狀態),並調用unparkSuccessor把該節點的后繼節點喚醒,如果該節點的狀態為0,則把狀態置為PROPAGATE。
2).共享模式同步狀態的釋放
該方法的實現同獨占模式類似,也是一個模板方法,具體的釋放實現由子類自定義,在成功釋放同步狀態后將會喚醒后繼節點:
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { //釋放同步狀態 doReleaseShared(); //喚醒后繼節點 return true; } return false; }
以上就是AQS中的共享模式對同步狀態的獲取與釋放,在有了獨占模式的分析過后,對共享模式的分析就顯得要輕松得多。下一節將會繼續探討AbstractQueuedSynchronizer類中具體的一些細節問題。