概述
Condition是Reerantlock 實現了 wait- notify 模式的類
synchronized鎖 wait- notify 模式 實現的 是 Object.wait(),Object.notify()。
so 重入鎖 Reerantlock 也要有wait- notify 模式 的實現,他就是 Condition。
執行 await 和signal 的前後必須有Reerantlock.lock()和Reerantlock.unlock,否則或拋出IllegalMonitorStateException異常。
Condition在ArrayBlockingQueue和 LinkedBlockingQueue中有重要應用。實現存數據和取數據方法之間的相互等待和喚醒。
Reerantlock中創建 Condition的方法
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
跟蹤發現 ConditionObject是 AbstractQueuedSynchronizer 的內部類。
源碼分析
await
await 和signal交互邏輯
ConditionObject中維護一個新的 waiter隊列,執行await後創建新節點入隊,然後釋放鎖,while循環檢查自己是否在鎖等待隊列中,沒有則進入阻塞。signal 按照FIFO的順序從waiter隊列中取出結點 ,入隊鎖等待隊列。然後 signal 所在的線程鎖釋放後,await 阻塞被釋放,執行後續處理。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();//入隊 waiter隊列
int savedState = fullyRelease(node);//釋放當前lock,執行await 需要釋放鎖。
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//檢查 當前節點是否在等待隊列中
LockSupport.park(this);//沒在 進入阻塞
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();//移除取消的節點
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
await執行分析: 初始化 waiter隊列,新創建節點 ,併入隊,然後釋放鎖,檢查自己是否在鎖等待隊列中,在隊列中就執行park阻塞,不在隊列中證明 singal已經將 剛纔入隊的節點 取出放入了等待隊列,無需執行park阻塞,執行acquireQueued()根據節點狀態獲取鎖成功後,如果阻塞過程中執行了打斷,拋出異常,或記錄打斷狀態。
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();//從頭到尾移除被取消的節點
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)//如果尾部是null,初始化節點
firstWaiter = node;
else//將節點 掛到尾部
t.nextWaiter = node;
lastWaiter = node;
return node;
}
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
在await過程中執行了打斷,如果是在signal前執行,說明 await在執行LockSupport.park(this);中被打斷,需要拋出異常,如果是在signal後執行,打斷沒有起作用要重新打斷。
//將取消條件等待的結點從條件隊列轉移到同步隊列中
final boolean transferAfterCancelledWait(Node node) {
//如果這步CAS操作成功的話就表明中斷髮生在signal方法之前
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//狀態修改成功後就將該結點放入同步隊列尾部
enq(node);//上面的cas操作執行後,再執行signal 就會跳過這個node。
return true;
}
//到這裏表明CAS操作失敗, 說明中斷髮生在signal方法之後
while (!isOnSyncQueue(node)) {
//如果sinal方法還沒有將結點轉移到同步隊列, 就通過自旋等待一下
Thread.yield();
}
return false;
}
signal
public final void signal() {
if (!isHeldExclusively())//檢查是否持有互斥鎖,沒有就拋出異常
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {//從 firstWaiter 開始向後 出隊節點
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;//全部出隊完成,更新lastWaiter
first.nextWaiter = null;
} while (!transferForSignal(first) &&(first = firstWaiter) != null);
//將waiter隊列中的node轉移到 鎖等待隊列中,一直循環直到成功一次或waiter隊列空了
}
doSignal 就是將waiter隊列中的node轉移到 鎖等待隊列中,一致循環直到成功一次或waiter隊列空了。
final boolean transferForSignal(Node node) {
//如果成功,可以繼續執行,否則證明 這個node被取消了
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);//入隊鎖等待隊列,await的阻塞 等待鎖釋放後被喚醒
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
doSignalAll
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;// 移除 共享變量對隊列的關聯,爲 出隊 waiter隊列中全部節點做準備。
do {
//獲取下個節點 爲後續循環獲取節點最準備
Node next = first.nextWaiter;
//unlink first
first.nextWaiter = null;
transferForSignal(first);//轉移first到鎖等待隊列中
first = next;//first 出隊,first指向next節點
} while (first != null);
}
doSignalAll 要點分析:
doSignalAll 是將waiter中的node全部轉移到等待隊列中,如果多個線程已經執行了await. 執行
doSignalAll 後,await會按照 waiter隊列中入隊的順序獲得鎖,排隊執行。
注意鏈表中節點被出隊的邏輯 先unlink node from queue, 命名一個變量指向這個node,使用結束後,這個變量指向null。然後這個節點纔會被GC掉。 GC的條件:沒有任何一個對象和變量名能訪問到這個對象實例。在隊列中 node被回收的條件:隊列中節點 沒有任何個對象和變量名能訪問到這個node.
疑問分析
Condition 的await和signal是怎樣拋出 java.lang.IllegalMonitorStateException的?
Reerantlock 的lock的方法 在獲取鎖後會save互斥鎖持有線程,就是執行lock的當前線程。
signal()執行前會檢查這個變量,拋出IllegalMonitorStateException。
await() 執行過程中需要釋放鎖,釋放鎖的方法中會檢查這個變量,拋出IllegalMonitorStateException
Reerantlock-lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
先看一下signal中的 IllegalMonitorStateException檢查
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//...
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
再看一下await中的 IllegalMonitorStateException檢查
public final void await() throws InterruptedException {
//...
int savedState = fullyRelease(node);
//...
}
final int fullyRelease(Node node) {
//...
if (release(savedState))
//...
}
public final boolean release(int arg) {
if (tryRelease(arg)) {//調用的是Reerantlock 的方法
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
Reerantlock 釋放方法檢測IllegalMonitorStateException
protected final boolean tryRelease(int releases) {
//...
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//...
}
說明:ConditionObject是AbstractQueuedSynchronize的 內部類,Reerantlock 繼承了AbstractQueuedSynchronize ,執行new ConditionObject() 實際上是創建了Reerantlock 內部類 ConditionObject的實例,so 這裏 訪問tryRelease是 Reerantlock 的實現
總結:Condition提供了 wait-notify機制的API,其實現類ConditionObject基於基於Reetrantlock實現。ConditionObject 本身自維護一個新的等待隊列,執行await時創建節點鏈接到這個隊列尾部,進入阻塞,執行signal時從隊列頭部 取出一個節點放入 Reetrantlock 的鎖等待隊列。執行Reetrantlock.unlock釋放 await中被阻塞的線程,await被釋放後 通過 Reetrantlock 的acquireQueue 排隊獲取鎖。
整個Condition功能的實現 簡單的概括: await在新隊列中加入節點,signal從新隊列中取出節點放入AQS的等待隊列,藉助AQS的加鎖,釋放,acquireQueue 排隊獲取鎖 實現了 wait-notify機制。