一:概念
共享式獲取與獨占式獲取最主要的區別在於同一時刻能否有多個線程同時 獲取到同步狀態。以讀寫為例,如果一個程序在進行讀操作,那么這一時刻寫操 作均被阻塞,
而讀操作能夠同時進行。寫操作要求對資源的獨占式訪問,而讀操 作可以是共享式訪問。
二:通過countDownLatch計數器的使用來分析
共享鎖的實現原理
定義一個計數器,初始計數值為5:
還是通過state進行標記的,只不過獨占鎖state表示線程的重入次數,而共享鎖表示計數器的次數
調用await方法,則該線程將被阻塞
這里面有個嘗試獲取共享鎖,如果當前計數器為0,則返回1,表示可以拿到共享鎖,否則返回-1,表示不可以拿到。
如果返回1,則當前線程繼續向下執行,不會被阻塞。如果為-1 則執行
doAcquireSharedInterruptibly
這里也是入對列,創建一個節點,如果當前節點的前驅節點正在執行,則嘗試獲取共享鎖,獲取到鎖則傳播喚醒下個線程,
獲取不到則進入對列阻塞,等待被喚醒。
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
將當前節點設置為head,並且釋放共享鎖:
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 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; if (s == null || s.isShared()) doReleaseShared(); } }
因為是共享鎖,所以當一個線程拿到共享鎖之后,會喚醒后續線程,然后后續線程再喚醒下一個節點。
來看一下countDown方法的實現:
如果countDownlatch的state減為0,則調用
doReleaseShared方法,釋放阻塞的線程。