本章我們繼續將Condition的最后一個方法signal方法,如果前面沒有看過的可以點擊LockSupport與Condition解析來看看Condition解讀的前半部分。
signal方法:
public final void signal() {if (!AbstractQueuedLongSynchronizer.this.isHeldExclusively()) {throw new IllegalMonitorStateException();} else {Node var1 = this.firstWaiter;if (var1 != null) {this.doSignal(var1);}}}
signal方法的主要作用就是將線程從Condition隊列中喚醒,前面已經講述過在Condtion的子類ConditionObject內部通過鏈表來維護整個Condtion隊列,并且含有兩個屬性firstWaiter和lastWaiter分別表示隊列頭和隊列尾部,分析方法首先進行檢查當前線程是否持有獨占鎖。目的是保證持有鎖的線程才能調用signal方法來喚醒線程,通過判斷之后開始從Condition隊列中取出隊首線程,隨后開始調用doSignal方法來喚醒線程
private void doSignal(Node var1) {do {if ((this.firstWaiter = var1.nextWaiter) == null) {this.lastWaiter = null;}var1.nextWaiter = null;} while(!AbstractQueuedLongSynchronizer.this.transferForSignal(var1) && (var1 = this.firstWaiter) != null);}
在dosignal中首先將后面的node設置為鏈表頭部,如果后續沒有node則將尾鏈表置為null。
同時調用transferForSignal(first)
嘗試將節點轉移到同步隊列,如果轉移失敗(返回false)且隊列還有節點(firstWaiter != null),繼續處理下一個節點。
下面我們來看看transferForSignal
方法是如何進行轉移的。
final boolean transferForSignal(Node var1) {if (!compareAndSetWaitStatus(var1, -2, 0)) {return false;} else {Node var2 = this.enq(var1);int var3 = var2.waitStatus;if (var3 > 0 || !compareAndSetWaitStatus(var2, var3, -1)) {LockSupport.unpark(var1.thread);}return true;}}
首先進行狀態位的CAS設置,如果無法設置表明狀態已經改變了直接返回false,表示無法入隊。
之后進行入隊enq操作(內部是一個循環不斷的CAS操作保證能入隊)入隊完畢之后則查看當前節點狀態如果還是阻塞狀態則直接調用unpark來喚醒當前線程。
1. 先判斷當前線程是否持有當前鎖沒有則拋出異常
2. 取出Condition隊列中的一個首節點嘗試入隊和喚醒操作
3.失敗則再次循環從隊伍中取出節點
4.在嘗試入隊的方法總首先會判斷狀態值是否符合不符合則直接返回false,符合則會通過CAS循環入隊操作,最后判斷狀態是否為阻塞,為阻塞則直接調用unpark方法進行喚醒操作。
至此Condtion的兩個方法已經介紹完畢。
總結:
await方法:
signal方法:
設計精髓
-
雙隊列分離:條件隊列(等待條件)與同步隊列(競爭鎖)分離
-
狀態驅動:
waitStatus
?精確控制節點生命周期 -
無鎖算法:CAS 操作保證線程安全
-
協作式喚醒:前驅節點負責喚醒后繼
-
資源繼承:await() 返回時自動恢復原始鎖狀態
典型應用場景
-
生產者-消費者:不同條件控制隊列空/滿
-
線程池任務調度:工作線程等待任務到達
-
資源池管理:連接可用性通知
-
屏障實現:所有線程到達后同時釋放
-
狀態機轉換:特定狀態變更觸發操作