前篇博客LZ已經分析了ReentrantLock的lock()實現過程,我們了解到lock實現機制有公平鎖和非公平鎖,兩者的主要區別在于公平鎖要按照CLH隊列等待獲取鎖,而非公平鎖無視CLH隊列直接獲取鎖。但是對于unlock()而已,它是不分為公平鎖和非公平鎖的。
public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
release(1),嘗試在當前鎖的鎖定計數(state)值上減1。成功返回true,否則返回false。當然在release()方法中不僅僅只是將state - 1這么簡單,- 1之后還需要進行一番處理,如果-1之后的新state = 0 ,則表示當前鎖已經被線程釋放了,同時會喚醒線程等待隊列中的下一個線程,當然該鎖不一定就一定會把所有權交給下一個線程,能不能成功就看它是不是親爹生的了(看運氣)。
PRotected final boolean tryRelease(int releases) { int c = getState() - releases; //state - 1 //判斷是否為當前線程在調用,不是拋出IllegalMonitorStateException異常 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //c == 0,釋放該鎖,同時將當前所持有線程設置為null if (c == 0) { free = true; setExclusiveOwnerThread(null); } //設置state setState(c); return free; }
在release代碼中有一段代碼很重要:
Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true;
對于這個LZ在前篇博客已經較為詳細的闡述了。不懂或者忘記請再次翻閱:【Java并發編程實戰】-----“J.U.C”:ReentrantLock之二lock方法分析
waitStatus!=0表明或者處于CANCEL狀態,或者是置SIGNAL表示下一個線程在等待其喚醒。也就是說waitStatus不為零表示它的后繼在等待喚醒。
unparkSuccessor()方法:
private void unparkSuccessor(Node node) { int ws = node.waitStatus; //如果waitStatus < 0 則將當前節點清零 if (ws < 0) compareAndSetWaitStatus(node, ws, 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); }
注:unlock最好放在finally中!!!!!!unlock最好放在finally中!!!!!! unlock最好放在finally中!!!!!! (重要的事說三遍)
參考文獻:
1、Java多線程系列--“JUC鎖”04之 公平鎖(二)
新聞熱點
疑難解答