Condition 原理解析
生活随笔
收集整理的這篇文章主要介紹了
Condition 原理解析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
流程
當調用wait 方法的時候從拿到一個最新創建的Node并加入 Condition 隊列
喚醒AQS隊列中的一個線程
判斷node是否在aqs 隊列上
如果不在的話將當前線程阻塞
當調用signal方法的時候會喚醒指定的線程 并添加當前節點到AQS隊列
wait()
public final void await() throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();//添加一個節點到Condition 隊列中 如果沒有則創建放到尾節點 尾插法 節點狀態為 condtitionNode node = addConditionWaiter();//釋放當前的鎖 得到鎖的狀態 并喚醒處于 aqs隊列的一個線程long savedState = fullyRelease(node);int interruptMode = 0;//判斷一個節點是否在aqs隊列上while (!isOnSyncQueue(node)) {//如果在 park自己阻塞等待LockSupport.park(this);//判斷自己是否被中斷if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}//嘗試獲取鎖if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;//如果node 的下一個節點不是null 清理condition隊列上的節點if (node.nextWaiter != null) // clean up if cancelledunlinkCancelledWaiters();if (interruptMode != 0)//線程中斷拋異常reportInterruptAfterWait(interruptMode);}創建Node 節點并加入Condition隊列中(單向鏈表)
如果node 是空則創建一個新的node狀態為 CONDITION并設置成尾節點
如果不是空并且節點狀態不是 CONDITION 則從鏈表中刪除 然后直接返回尾節點
如果不是空節點并且節點狀態是 CONDITION 則把尾節點的下一個節點設置成剛構建好的節點
刪除不是CONDITION狀態的節點
private void unlinkCancelledWaiters() {Node t = firstWaiter;Node trail = null;// 如果首節點不為空while (t != null) {// 獲取到下個節點Node next = t.nextWaiter;// 如果該節點的狀態不等于conditon,則該節點需要在鏈表中刪除if (t.waitStatus != Node.CONDITION) {// 該節點的下個節點設置為空,意味著垃圾回收后就回收該節點t.nextWaiter = null;// trail 為空,則把下一個節點負責給首節點if (trail == null)firstWaiter = next;else// 把下一個節點賦值給next,這樣鏈表就要繼續連接起來trail.nextWaiter = next;if (next == null)lastWaiter = trail;}// 等于condtion,把該節點賦值給尾節點elsetrail = t;// 下個一個節點賦值給t,進行下一次循環t = next;}}喚醒AQS隊列中的一個線程
final long fullyRelease(Node node) {boolean failed = true;try {//拿當前鎖的狀態值long savedState = getState();//釋放鎖if (release(savedState)) {failed = false;return savedState;} else {throw new IllegalMonitorStateException();}} finally {if (failed)node.waitStatus = Node.CANCELLED;}} protected final boolean tryRelease(int releases) {//state -1int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {//如果c =0 表示當前是無鎖狀態 把線程iq清空free = true;setExclusiveOwnerThread(null);}//重新設置 statesetState(c);return free;} private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling. It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)//設置head節點的狀態為0 compareAndSetWaitStatus(node, ws, 0);/** Thread to unpark is held in successor, which is normally* just the next node. But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*///拿到head節點的下一個節點Node s = node.next;//如果下一個節點為null 或者 status>0則表示是 CANCELLED 狀態//聽過尾部節點開始掃描 找到距離 head最近的一個 waitStatus<=0的節點if (s == null || s.waitStatus > 0) {s = null;for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}//如果next 節點不等于空直接喚醒這個線程if (s != null)LockSupport.unpark(s.thread);}判斷當前節點是否在同步隊列中,返回 false 表示不在,返回true 表示如果不在,將當前線程掛起
final boolean isOnSyncQueue(Node node) {// 如果狀態是condition,證明一定不再同步隊列里,condition狀態只存在于等待隊列,在同步隊列里,node.prev是一定不為空的,因為有個head的節點if (node.waitStatus == Node.CONDITION || node.prev == null)return false;// 在等待隊列里,node.next 是等于空的,不等于空就是在同步隊列當中if (node.next != null) // If has successor, it must be on queuereturn true;// 遍歷正個同步隊列,判斷node是否在同步隊列當中return findNodeFromTail(node);}比如由于線程A調用了await 方法然后進入阻塞狀態并喚醒了處于aqs 隊列中的線程B此時線程B執行調用 signal 會喚醒處于 Condition 隊列中阻塞等待的節點signal
public final void signal() {// 判斷當前線程是否獲取到了鎖,如果沒有拋異常if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;// 如果首節點不為空,喚醒首節點if (first != null)doSignal(first);}doSignal
private void doSignal(Node first) {do {// frist的下一個節點如果為空,就把lastWaiter設置為空if ( (firstWaiter = first.nextWaiter) == null)lastWaiter = null;// 不為空,再把first 節點從等待隊列中移除first.nextWaiter = null;} while (!transferForSignal(first) &&// 返回false,firstWaiter已經被從新賦值過了,如果不是空,進行下一次遍歷(first = firstWaiter) != null);}transferForSignal
final boolean transferForSignal(Node node) {// 如果該節點不是condition狀態(可能編程了cancelled狀態),waitStatus=0,就會設置失敗,返回falseif (!compareAndSetWaitStatus(node, Node.CONDITION, 0))return false;// 將該節點放入到AQS隊列中Node p = enq(node);int ws = p.waitStatus;// 如果上一個節點狀態沒有被改變,也就是沒有編程cancelled狀態,就將該節點狀態設置成singal狀態if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))// 如果狀態已經是cancelled狀態,將該節點的線程掛起LockSupport.unpark(node.thread);return true;}總結
以上是生活随笔為你收集整理的Condition 原理解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php(thinkphp)在linux系
- 下一篇: Favorite Setting