公平鎖解鎖
解鎖可以分解成兩個步驟:
- 解鎖
- 喚醒等待線程
// 解鎖操作
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
// 嘗試解鎖
if (tryRelease(arg)) {
Node h = head;
// 如果等待隊(duì)列不為空
if (h != null && h.waitStatus != 0)
// 準(zhǔn)備喚醒等待線程
unparkSuccessor(h);
// 解鎖成功
return true;
}
// 解鎖失敗
return false;
}
- 解鎖
// 下面邏輯不存在并發(fā)問題,因?yàn)楫?dāng)前線程已經(jīng)獲取了鎖
protected final boolean tryRelease(int releases) {
// 計(jì)算解鎖后的state
int c = getState() - releases;
// 如果解鎖線程和持有鎖的線程不是一個線程,那么直接拋異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c解鎖后等于0,那么說明這個線程完全不持有鎖了
if (c == 0) {
// 解鎖成功
free = true;
// 把持有鎖的線程引用置為null
setExclusiveOwnerThread(null);
}
// 設(shè)置解鎖后state值。
// 這個解鎖有可能是在多次lock后執(zhí)行unlock
// 這樣的話,持有鎖的線程還是當(dāng)前線程,只不過state減少
setState(c);
// 返回解鎖結(jié)果
return free;
}
示例:
// 創(chuàng)建一個公平鎖
ReentrantLock lock = new ReentrantLock(true);
try{
// 競爭鎖
lock.lock();
// 執(zhí)行業(yè)務(wù)邏輯
System.out.println("hello world");
}finally{
// 解鎖
// state=0,并且setExclusiveOwnerThread(null)
lock.unlock();
}
// 創(chuàng)建一個公平鎖
ReentrantLock lock = new ReentrantLock(true);
try{
// 競爭鎖
lock.lock();
// 執(zhí)行業(yè)務(wù)邏輯
System.out.println("hello world");
try{
// 鎖重入
lock.lock();
// 執(zhí)行業(yè)務(wù)邏輯
System.out.println("再次獲取鎖");
}finally{
// 第一次解鎖。
// state=state-1
lock.unlock();
}
}finally{
// 再次解鎖。
// state=0, setExclusiveOwnerThread(null)
lock.unlock();
}
- 喚醒等待線程
private void unparkSuccessor(Node node) {
// 獲取頭節(jié)點(diǎn)狀態(tài)
int ws = node.waitStatus;
// 如果head節(jié)點(diǎn)狀態(tài)小于0,
if (ws < 0)
// 把頭節(jié)點(diǎn)狀態(tài)設(shè)置為0
node.compareAndSetWaitStatus(ws, 0);
// 獲取頭節(jié)點(diǎn)下一個節(jié)點(diǎn)
Node s = node.next;
// 如果下一個節(jié)點(diǎn)為空,或者下一個節(jié)點(diǎn)狀態(tài)是“取消”狀態(tài)
if (s == null || s.waitStatus > 0) {
s = null;
// 通過從后向前遍歷,尋找最靠近頭節(jié)點(diǎn)的狀態(tài)小于等于0的節(jié)點(diǎn)
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
// 如果存在靠近頭節(jié)點(diǎn),并且狀態(tài)小于等于0的節(jié)點(diǎn)
// 那么直接喚醒該節(jié)點(diǎn)
LockSupport.unpark(s.thread);
}
在上面的解鎖過程中,一直沒有看到任何處理head節(jié)點(diǎn)的代碼。理論上來說,head節(jié)點(diǎn)在解鎖后就應(yīng)該沒有任何變量引用它,那么head節(jié)點(diǎn)是在什么時候被處理掉的呢?
其實(shí)答案在下面的代碼中:
final boolean acquireQueued(final Node node, int arg) {
// 默認(rèn)線程未被打斷
boolean interrupted = false;
try {
// 開啟自旋
for (;;) {
// 獲取當(dāng)前節(jié)點(diǎn)的前一個節(jié)點(diǎn)
final Node p = node.predecessor();
// 如果前一個節(jié)點(diǎn)是head節(jié)點(diǎn),那么就嘗試競爭鎖
if (p == head && tryAcquire(arg)) {
// 競爭鎖成功,把當(dāng)前節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn)
setHead(node);
// 把前一個節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)斷開
// 因?yàn)楫?dāng)前節(jié)點(diǎn)已經(jīng)設(shè)置為head節(jié)點(diǎn)了,之前的head就可以GC了
p.next = null; // help GC
// 返回是否當(dāng)前線程被打斷。
// 這個返回結(jié)果的作用會被用在lockInterruptibly()這個方法上。
// lock()方法可忽略。
return interrupted;
}
// 判斷當(dāng)前節(jié)點(diǎn)是否應(yīng)該阻塞。
if (shouldParkAfterFailedAcquire(p, node))
// 下面這個代碼可以翻譯成:
// if(parkAndCheckInterrupt()){
// interrupted = true;
// }
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
// 拋出任何異常,都直接取消當(dāng)前節(jié)點(diǎn)正在競爭鎖的操作
// 如果在等待隊(duì)列中,就從等待隊(duì)列中移除。
// 如果當(dāng)前線程已經(jīng)搶占到鎖了,那么就解鎖。
cancelAcquire(node);
// 如果當(dāng)前線程已經(jīng)被中斷
if (interrupted)
// 重新設(shè)置中斷信號
selfInterrupt();
// 拋出當(dāng)前異常
throw t;
}
}
當(dāng)?shù)却?duì)列中的節(jié)點(diǎn)競爭到鎖后,會把自己變成head節(jié)點(diǎn),之前的head節(jié)點(diǎn)就斷開所有的引用,直至被GC。