佇列同步器 獨佔式同步狀態獲取與釋放

2021-08-18 12:36:11 字數 2609 閱讀 6380

佇列同步器的實現依賴內部的同步佇列來完成同步狀態的管理。它是乙個fifo的雙向佇列,當執行緒獲取同步狀態失敗時,同步器會將當前執行緒和等待狀態等資訊包裝成乙個節點並將其加入同步佇列,同時會阻塞當前執行緒。當同步狀態釋放時,會把頭節點中的執行緒喚醒,使其再次嘗試獲取同步狀態。

node靜態內部類的原始碼

static final class node 

final node predecessor() throws nullpointerexception

node()

node(thread thread, node mode)

node(thread thread, int waitstatus)

}

同步器包含了兩個節點型別的引用,乙個指向頭節點,而另乙個指向尾節點。

acquire(int arg)方法為aqs提供的模板方法,該方法為獨佔式獲取同步狀態,但是該方法對中斷不敏感,也就是說由於執行緒獲取同步狀態失敗加入到clh同步佇列中,後續對執行緒進行中斷操作時,執行緒不會從同步佇列中移除。**如下:

public final void acquire(int arg)
acquire()說明

(01) 先是通過tryacquire()嘗試獲取鎖。獲取成功的話,直接返回;嘗試失敗的話,再通過acquirequeued()獲取鎖。

(02) 嘗試失敗的情況下,會先通過addwaiter()來將「當前執行緒」加入到"clh佇列"末尾;然後呼叫acquirequeued(),在clh佇列中排序等待獲取鎖,在此過程中,執行緒處於休眠狀態。直到獲取鎖了才返回。 如果在休眠等待過程中被中斷過,則呼叫selfinterrupt()來自己產生乙個中斷。

/**

* 「當前執行緒」自己產生乙個中斷

*/private static void selfinterrupt()

說明:selfinterrupt()的**很簡單,就是「當前執行緒」自己產生乙個中斷。但是,為什麼需要這麼做呢?

這必須結合acquirequeued()進行分析。如果在acquirequeued()中,當前執行緒被中斷過,則執行selfinterrupt();否則不會執行。

在acquirequeued()中,即使是執行緒在阻塞狀態被中斷喚醒而獲取到cpu執行權利;但是,如果該執行緒的前面還有其它等待鎖的執行緒,根據公平性原則,該執行緒依然無法獲取到鎖。它會再次阻塞! 該執行緒再次阻塞,直到該執行緒被它的前面等待鎖的執行緒鎖喚醒;執行緒才會獲取鎖,然後「真正執行起來」!

也就是說,在該執行緒「成功獲取鎖並真正執行起來」之前,它的中斷會被忽略並且中斷標記會被清除! 因為在parkandcheckinterrupt()中,我們執行緒的中斷狀態時呼叫了thread.interrupted()。該函式不同於thread的isinterrupted()函式,isinterrupted()僅僅返回中斷狀態,而interrupted()在返回當前中斷狀態之後,還會清除中斷狀態。 正因為之前的中斷狀態被清除了,所以這裡需要呼叫selfinterrupt()重新產生乙個中斷!

2.1 addwaiter()方法

private node addwaiter(node mode) 

}enq(node);

return node;

}

private node enq(final node node) else } }

}

2.2 acquirequeued()方法

final boolean acquirequeued(final node node, int arg) 

if (shouldparkafte***iledacquire(p, node) &&

(parkandcheckinterrupt))

interrupted = true;

}} finally

}

/**

* 設定同步狀態獲取失敗後的節點狀態,並返回"當前執行緒是否應該阻塞",

*/private static boolean shouldparkafte***iledacquire(node pred, node node) while (pred.waitstatus > 0);

pred.next = node;

} else

return false;

}

/**

* 阻塞當前執行緒,並且返回「執行緒被喚醒之後」的中斷狀態

*/private final boolean parkandcheckinterrupt()

public final boolean release(int arg) 

return false;

}

private void unparksuccessor(node node) 

if (s != null)

locksupport.unpark(s.thread); //喚醒node的後繼節點

}

獨佔式同步狀態獲取與釋放

通過呼叫同步器的acquire int arg 方法可以獲取同步狀態,該方法對中斷不敏感,也就是由於執行緒獲取同步狀態失敗後進入同步佇列中,後續對執行緒進行中斷操作時,執行緒不會從同步佇列中移出。該方法 清單如下。public final void acquire int arg 分析節點的構造以及...

佇列同步器

1 abstractqueuedsynchronizer 頭節點 獲取同步狀態成功的節點 尾節點 執行緒無法獲取到同步狀態,而被構造成節點,加入到同步佇列。加入佇列必須保證執行緒安全 compareandsettail node expect,node update 2 獨佔式同步 1 獲取同步狀態...

AQS(獲取資源 獨佔模式)2) 佇列同步器

1.獲取資源 獨佔模式 acquire int 首先講解獨佔模式 exclusive 下的獲取 釋放資源過程,其入口方法為 public final void acquire int arg tryacquire arg 為執行緒獲取資源的方法函式,在aqs中定義如下 protected boole...