JUC源碼分析-Reetrantlock-Condition

概述

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機制。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章