下面來看ReentrantLock.unlock,嘗試在目前鎖的鎖定計數即state值上減1,而lock每次把鎖定計數加1,這也是為什麼lock和unlock必須成對出現,否則鎖定計數就不能正常恢複到0,其它線程就不能嘗試擷取鎖
public void unlock() {
sync.release( 1 );
}
AbstractQueuedsynchronizer.release(int arg) 方法會在鎖定數目上減去arg,若新鎖定數目為0,表示鎖被目前線程釋放, 則試圖喚醒等待隊列中的下一個線程。請注意這裡僅是喚醒,而非把鎖的所有權交給下一個線程。該線程能否成功擷取鎖,還要看運氣。
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定義在Sync類中
Sync.tryRelease會在目前鎖定狀态上減去要釋放的鎖定數,如果鎖定狀态為零則置目前持有鎖線程為null,傳回true,否則傳回false
此外還會檢查是否是占有鎖的線程在調用,若不是則抛出IllegalMonitorStateException。
protected final boolean tryRelease( int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false ;
if (c == 0 ) {
free = true ;
setExclusiveOwnerThread( null );
}
setState(c);
return free;
}
再來看unparkSuccessor
private void unparkSuccessor(Node node) {
// 若目前節點waitStatus被置為SIGNAL則清零
compareAndSetWaitStatus(node, Node.SIGNAL, 0 );
// 若後續節點為空或已被cancel,則從尾部開始找到隊列中第一個waitStatus<=0,即未被cancel的節點
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);
}
再回頭看AbstractQueuedSynchronizer.acquireQueued,注釋1,2,3,4表明了線上程被喚醒後的執行順序
final boolean acquireQueued( final Node node, int arg) {
try {
boolean interrupted = false ;
for (;;) {
// 2 因為等待隊列隻會從尾部插入,是以在被喚醒節點和頭節點之間不會新加入等待節點,隻會有節點被cancel
// 3 如果被喚醒節點不是頭節點的直接後續,由喚醒算法可以保證其和頭結點之間沒有waitStatus<=0的節點,但有可能有waitStatus大于0,即被CANCEL的節點,參見注釋4
// 5擷取鎖成功則把目前節點設定為頭節點,傳回,否則線程繼續被禁止,直到下一次被喚醒
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null ; // help GC
return interrupted;
}
// 1 被lock.release喚醒,parkAndCheckInterrupt傳回false,繼續回到for循環開始
// 4 被cancel的結點在shouldParkAfterFailedAcquire中會被清理掉,目前節點會成為頭節點的直接後繼,然後return false,在下次循環中即可嘗試擷取鎖
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true ;
}
} catch (RuntimeException ex) {
cancelAcquire(node);
throw ex;
}
}