signal和signalAll大同小異,本節(jié)我們來看下signal函數(shù),signal核心調(diào)用的是doSignal,所以signal函數(shù)就不貼了:
//doSignal只做了一件事,將wait隊列中的節(jié)點移出道aqs的等待隊列中
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;//這里是個出隊的過程,將對頭從隊列中移出
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
* 看作者注解,如果不能將waitStatus設置成0,表示該節(jié)點是取消的,返回false
* 這里有兩種情況:
* 1)ws的值不是condition(-2),說明已經(jīng)被改變了
* 2)cas失敗,說明有另一個線程也同時在signal,
* 不管哪種情況該node一定會被喚醒
* 這里返回false,看上面函數(shù)的while進入循環(huán),拿到下一個節(jié)點,繼續(xù)
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//入aqs的等待隊列
Node p = enq(node);
int ws = p.waitStatus;
//ws>0說明不是正常等待狀態(tài)(一般是cancel),可以看下面的源碼注解
//或者cas ws失敗則喚醒node線程
//注意這里的p是node的前置節(jié)點,假如cas成功,說明前置節(jié)點也是在等待狀態(tài),那么就不需要喚醒node線程(因為前置都在等待,node也一定是等待,等走aqs的正常流程喚醒就好了)
//如果cas失敗說明ws正在被其他線程修改,這里又有2種情況:
//1)ws被修改成1(cancel),那么喚醒線程沒問題
//2)ws被修改成0,也就是p已經(jīng)獲得到鎖,這樣unpark后會讓代碼執(zhí)行到acquireQueued()去cas嘗試獲取鎖,以此來提高效率
//再看如果ws<=0表示正常等待狀態(tài),不去喚醒線程會不會有問題?
//那么線程會一直掛起直到被p喚醒,重新進入while (!isOnSyncQueue(node)) 這個時候一定在aqs的等待隊列中,繼續(xù)往下進入acquireQueued()去獲取鎖
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;