1.?Condition的signal方法分析
分為了幾個部分:
● 確保執行signal方法的是持有鎖的線程
● 脫離Condition的隊列
● 將Node狀態從-2改為0
● 將Node添加到AQS隊列
● 為了避免當前Node無法在AQS隊列正常喚醒做了一些判斷和操作
// 線程掛起后,可以基于signal喚醒~
public final void signal() {// 在ReentrantLock中,如果執行signal的線程沒有持有鎖資源,直接扔異常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 拿到排在Condition首位的Node
Node first = firstWaiter;
// 有Node在排隊,才需要喚醒,如果沒有,直接告辭~~
if (first != null)
doSignal(first);
}
// 開始喚醒Condition中的Node中的線程
private void doSignal(Node first) {
// 先一波do-while走你~~~
do {
// 獲取到第二個節點,并且將第二個節點設置為firstWaiter
if ( (firstWaiter = first.nextWaiter) == null)
// 說明就一個節點在Condition隊列中,那么直接將firstWaiter和lastWaiter置位null
lastWaiter = null;
// 如果還有nextWaiter節點,因為當前節點要被喚醒了,脫離整個Condition隊列。將nextWaiter置位null
first.nextWaiter = null;
// 如果transferForSignal返回true,一切正常,退出while循環
} while (!transferForSignal(first) &&
// 如果后續節點還有,往后面繼續喚醒,如果沒有,退出while循環
(first = firstWaiter) != null);
}
// 準備開始喚醒在Condition中排隊的Node
final boolean transferForSignal(Node node) {
// 將在Condition隊列中的Node的狀態從-2,改為0,代表要扔到AQS隊列了。
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
// 如果失敗了,說明在signal之前應當是線程被中斷了,從而被喚醒了。
return false;
// 如果正常的將Node的狀態從-2改為0,這是就要將Condition中的這個Node扔到AQS的隊列。
// 將當前Node扔到AQS隊列,返回的p是當前Node的prev
Node p = enq(node);
// 獲取上一個Node的狀態
int ws = p.waitStatus;
// 如果ws > 0 ,說明這個Node已經被取消了。
// 如果ws狀態不是取消,將prev節點的狀態改為-1,。
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 如果prev節點已經取消了,可能會導致當前節點永遠無法被喚醒。立即喚醒當前節點,基于acquireQueued方法,
// 讓當前節點找到一個正常的prev節點,并掛起線程
// 如果prev節點正常,但是CAS修改prev節點失敗了。證明prev節點因為并發原因導致狀態改變。還是為了避免當前
// 節點無法被正常喚醒,提前喚醒當前線程,基于acquireQueued方法,讓當前節點找到一個正常的prev節點,并掛起線程
LockSupport.unpark(node.thread);
// 返回true
return true;
}
2.?Conditiond的await方法分析(后置分析)
分為了幾個部分:
● 喚醒之后,要先確認是中斷喚醒還是signal喚醒,還是signal喚醒后被中斷
● 確保當前線程的Node已經在AQS隊列中
● 執行acquireQueued方法,等待鎖資源。
● 在獲取鎖資源后,要確認是否在獲取鎖資源的階段被中斷過,如果被中斷過,并且不是THROW_IE,那就確保
interruptMode是REINTERRUPT。
● 確認當前Node已經不在Condition隊列中了
● 最終根據interruptMode來決定具體做的事情
● 0:嘛也不做。
● THROW_IE:拋出異常
● REINTERRUPT:執行線程的interrupt方法
// 現在分析await方法的后半部分
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// 中斷模式~
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 如果線程執行到這,說明現在被喚醒了。
// 線程可以被signal喚醒。(如果是signal喚醒,可以確認線程已經在AQS隊列中)
// 線程可以被interrupt喚醒,線程被喚醒后,沒有在AQS隊列中。
// 如果線程先被signal喚醒,然后線程中斷了。。。。(做一些額外處理)
// checkInterruptWhileWaiting可以確認當前中如何喚醒的。
// 返回的值,有三種
// 0:正常signal喚醒,沒別的事(不知道Node是否在AQS隊列)// THROW_IE(-1):中斷喚醒,并且可以確保在AQS隊列
// REINTERRUPT(1):signal喚醒,但是線程被中斷了,并且可以確保在AQS隊列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// Node一定在AQS隊列
// 執行acquireQueued,嘗試在ReentrantLock中獲取鎖資源。
// acquireQueued方法返回true:代表線程在AQS隊列中掛起時,被中斷過
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
// 如果線程在AQS隊列排隊時,被中斷了,并且不是THROW_IE狀態,確保線程的interruptMode是REINTERRUPT
// REINTERRUPT:await不是中斷喚醒,但是后續被中斷過!!!
interruptMode = REINTERRUPT;
// 如果當前Node還在condition的單向鏈表中,脫離Condition的單向鏈表
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 如果interruptMode是0,說明線程在signal后以及持有鎖的過程中,沒被中斷過,什么事都不做!
if (interruptMode != 0)
// 如果不是0~
reportInterruptAfterWait(interruptMode);
}
// 判斷當前線程被喚醒的模式,確認interruptMode的值。
private int checkInterruptWhileWaiting(Node node) {
// 判斷線程是否中斷了。
return Thread.interrupted() ?
// THROW_IE:代表線程是被interrupt喚醒的,需要向上排除異常
// REINTERRUPT:代表線程是signal喚醒的,但是在喚醒之后,被中斷了。(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
// 線程是正常的被signal喚醒,并且線程沒有中斷過。
0;
}
// 判斷線程到底是中斷喚醒的,還是signal喚醒的!
final boolean transferAfterCancelledWait(Node node) {
// 基于CAS將Node的狀態從-2改為0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
// 說明是中斷喚醒的線程。因為CAS成功了。
// 將Node添加到AQS隊列中~(如果是中斷喚醒的,當前線程同時存在Condition的單向鏈表以及AQS的隊列中)
enq(node);
// 返回true
return true;
}
// 判斷當前的Node是否在AQS隊列(signal喚醒的,但是可能線程還沒放到AQS隊列)
// 等到signal方法將線程的Node扔到AQS隊列后,再做后續操作
while (!isOnSyncQueue(node))
// 如果沒在AQS隊列上,那就線程讓步,稍等一會,Node放到AQS隊列再處理(看CPU)
Thread.yield();
// signal喚醒的,返回false
return false;
}
// 確認Node是否在AQS隊列上
final boolean isOnSyncQueue(Node node) {
// 如果線程狀態為-2,肯定沒在AQS隊列// 如果prev節點的值為null,肯定沒在AQS隊列
if (node.waitStatus == Node.CONDITION || node.prev == null)
// 返回false
return false;
// 如果節點的next不為null。說明已經在AQS隊列上。、
if (node.next != null)
// 確定AQS隊列上有!
return true;
// 如果上述判斷都沒有確認節點在AQS隊列上,在AQS隊列中尋找一波
return findNodeFromTail(node);
}
// 在AQS隊列中找當前節點
private boolean findNodeFromTail(Node node) {
// 拿到尾節點
Node t = tail;
for (;;) {
// tail是否是當前節點,如果是,說明在AQS隊列
if (t == node)
// 可以跳出while循環
return true;
// 如果節點為null,AQS隊列中沒有當前節點
if (t == null)
// 進入while,讓步一手
return false;
// t向前引用
t = t.prev;
}
}
private void reportInterruptAfterWait(int interruptMode) throws InterruptedException {
// 如果是中斷喚醒的await,直接拋出異常!
if (interruptMode == THROW_IE)
throw new InterruptedException();
// 如果是REINTERRUPT,signal后被中斷過
else if (interruptMode == REINTERRUPT)
// 確認線程的中斷標記位是true
// Thread.currentThread().interrupt();
selfInterrupt();
}
3?Condition的awaitNanos&signalAll方法分析
awaitNanos:僅僅是在await方法的基礎上,做了一內內的改變,整體的邏輯思想都是一樣的。
掛起線程時,傳入要阻塞的時間,時間到了,自動喚醒,走添加到AQS隊列的邏輯
// await指定時間,多了個時間到了自動醒。
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// deadline:當前線程最多掛起到什么時間點final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// nanosTimeout的時間小于等于0,直接告辭!!
if (nanosTimeout <= 0L) {
// 正常扔到AQS隊列
transferAfterCancelledWait(node);
break;
}
// nanosTimeout的時間大于1000納秒時,才可以掛起線程
if (nanosTimeout >= spinForTimeoutThreshold)
// 如果大于,正常掛起
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
// 計算剩余的掛起時間,可能需要重新的走while循環,再次掛起線程
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
// 剩余的掛起時間
return deadline - System.nanoTime();
}
signalAll方法。這個方法一看就懂,之前signal是喚醒1個,這個是全部喚醒
// 以do-while的形式,將Condition單向鏈表中的所有Node,全部喚醒并扔到AQS隊列
private void doSignalAll(Node first) {
// 將頭尾都置位null~
lastWaiter = firstWaiter = null;
do {
// 拿到next節點的引用
Node next = first.nextWaiter;
// 斷開當前Node的nextWaiter
first.nextWaiter = null;
// 修改Node狀態,扔AQS隊列,是否喚醒!
transferForSignal(first);
// 指向下一個節點
first = next;
} while (first != null);
}