ConditionObject介紹(二)

1.?Condition的signal方法分析

分為了幾個部分:
● 確保執行signal方法的是持有鎖的線程
● 脫離Condition的隊列
● 將Node狀態從-2改為0
● 將Node添加到AQS隊列
● 為了避免當前Node無法在AQS隊列正常喚醒做了一些判斷和操作

// 線程掛起后,可以基于signal喚醒~
public final void signal() {// 在ReentrantLock中,如果執行signal的線程沒有持有鎖資源,直接扔異常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 拿到排在Condition首位的Node
Node first = firstWaiter;
// 有Node在排隊,才需要喚醒,如果沒有,直接告辭~~
if (first != null)
doSignal(first);
}
// 開始喚醒Condition中的Node中的線程
private void doSignal(Node first) {
// 先一波do-while走你~~~
do {
// 獲取到第二個節點,并且將第二個節點設置為firstWaiter
if ( (firstWaiter = first.nextWaiter) == null)
// 說明就一個節點在Condition隊列中,那么直接將firstWaiter和lastWaiter置位null
lastWaiter = null;
// 如果還有nextWaiter節點,因為當前節點要被喚醒了,脫離整個Condition隊列。將nextWaiter置位null
first.nextWaiter = null;
// 如果transferForSignal返回true,一切正常,退出while循環
} while (!transferForSignal(first) &&
// 如果后續節點還有,往后面繼續喚醒,如果沒有,退出while循環
(first = firstWaiter) != null);
}
// 準備開始喚醒在Condition中排隊的Node
final boolean transferForSignal(Node node) {
// 將在Condition隊列中的Node的狀態從-2,改為0,代表要扔到AQS隊列了。
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
// 如果失敗了,說明在signal之前應當是線程被中斷了,從而被喚醒了。
return false;
// 如果正常的將Node的狀態從-2改為0,這是就要將Condition中的這個Node扔到AQS的隊列。
// 將當前Node扔到AQS隊列,返回的p是當前Node的prev
Node p = enq(node);
// 獲取上一個Node的狀態
int ws = p.waitStatus;
// 如果ws > 0 ,說明這個Node已經被取消了。
// 如果ws狀態不是取消,將prev節點的狀態改為-1,。
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 如果prev節點已經取消了,可能會導致當前節點永遠無法被喚醒。立即喚醒當前節點,基于acquireQueued方法,
// 讓當前節點找到一個正常的prev節點,并掛起線程
// 如果prev節點正常,但是CAS修改prev節點失敗了。證明prev節點因為并發原因導致狀態改變。還是為了避免當前
// 節點無法被正常喚醒,提前喚醒當前線程,基于acquireQueued方法,讓當前節點找到一個正常的prev節點,并掛起線程
LockSupport.unpark(node.thread);
// 返回true
return true;
}

2.?Conditiond的await方法分析(后置分析)

分為了幾個部分:
● 喚醒之后,要先確認是中斷喚醒還是signal喚醒,還是signal喚醒后被中斷
● 確保當前線程的Node已經在AQS隊列中
● 執行acquireQueued方法,等待鎖資源。
● 在獲取鎖資源后,要確認是否在獲取鎖資源的階段被中斷過,如果被中斷過,并且不是THROW_IE,那就確保
interruptMode是REINTERRUPT。
● 確認當前Node已經不在Condition隊列中了
● 最終根據interruptMode來決定具體做的事情
● 0:嘛也不做。
● THROW_IE:拋出異常
● REINTERRUPT:執行線程的interrupt方法

// 現在分析await方法的后半部分
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// 中斷模式~
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 如果線程執行到這,說明現在被喚醒了。
// 線程可以被signal喚醒。(如果是signal喚醒,可以確認線程已經在AQS隊列中)
// 線程可以被interrupt喚醒,線程被喚醒后,沒有在AQS隊列中。
// 如果線程先被signal喚醒,然后線程中斷了。。。。(做一些額外處理)
// checkInterruptWhileWaiting可以確認當前中如何喚醒的。
// 返回的值,有三種
// 0:正常signal喚醒,沒別的事(不知道Node是否在AQS隊列)// THROW_IE(-1):中斷喚醒,并且可以確保在AQS隊列
// REINTERRUPT(1):signal喚醒,但是線程被中斷了,并且可以確保在AQS隊列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// Node一定在AQS隊列
// 執行acquireQueued,嘗試在ReentrantLock中獲取鎖資源。
// acquireQueued方法返回true:代表線程在AQS隊列中掛起時,被中斷過
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
// 如果線程在AQS隊列排隊時,被中斷了,并且不是THROW_IE狀態,確保線程的interruptMode是REINTERRUPT
// REINTERRUPT:await不是中斷喚醒,但是后續被中斷過!!!
interruptMode = REINTERRUPT;
// 如果當前Node還在condition的單向鏈表中,脫離Condition的單向鏈表
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 如果interruptMode是0,說明線程在signal后以及持有鎖的過程中,沒被中斷過,什么事都不做!
if (interruptMode != 0)
// 如果不是0~
reportInterruptAfterWait(interruptMode);
}
// 判斷當前線程被喚醒的模式,確認interruptMode的值。
private int checkInterruptWhileWaiting(Node node) {
// 判斷線程是否中斷了。
return Thread.interrupted() ?
// THROW_IE:代表線程是被interrupt喚醒的,需要向上排除異常
// REINTERRUPT:代表線程是signal喚醒的,但是在喚醒之后,被中斷了。(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
// 線程是正常的被signal喚醒,并且線程沒有中斷過。
0;
}
// 判斷線程到底是中斷喚醒的,還是signal喚醒的!
final boolean transferAfterCancelledWait(Node node) {
// 基于CAS將Node的狀態從-2改為0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
// 說明是中斷喚醒的線程。因為CAS成功了。
// 將Node添加到AQS隊列中~(如果是中斷喚醒的,當前線程同時存在Condition的單向鏈表以及AQS的隊列中)
enq(node);
// 返回true
return true;
}
// 判斷當前的Node是否在AQS隊列(signal喚醒的,但是可能線程還沒放到AQS隊列)
// 等到signal方法將線程的Node扔到AQS隊列后,再做后續操作
while (!isOnSyncQueue(node))
// 如果沒在AQS隊列上,那就線程讓步,稍等一會,Node放到AQS隊列再處理(看CPU)
Thread.yield();
// signal喚醒的,返回false
return false;
}
// 確認Node是否在AQS隊列上
final boolean isOnSyncQueue(Node node) {
// 如果線程狀態為-2,肯定沒在AQS隊列// 如果prev節點的值為null,肯定沒在AQS隊列
if (node.waitStatus == Node.CONDITION || node.prev == null)
// 返回false
return false;
// 如果節點的next不為null。說明已經在AQS隊列上。、
if (node.next != null)
// 確定AQS隊列上有!
return true;
// 如果上述判斷都沒有確認節點在AQS隊列上,在AQS隊列中尋找一波
return findNodeFromTail(node);
}
// 在AQS隊列中找當前節點
private boolean findNodeFromTail(Node node) {
// 拿到尾節點
Node t = tail;
for (;;) {
// tail是否是當前節點,如果是,說明在AQS隊列
if (t == node)
// 可以跳出while循環
return true;
// 如果節點為null,AQS隊列中沒有當前節點
if (t == null)
// 進入while,讓步一手
return false;
// t向前引用
t = t.prev;
}
}
private void reportInterruptAfterWait(int interruptMode) throws InterruptedException {
// 如果是中斷喚醒的await,直接拋出異常!
if (interruptMode == THROW_IE)
throw new InterruptedException();
// 如果是REINTERRUPT,signal后被中斷過
else if (interruptMode == REINTERRUPT)
// 確認線程的中斷標記位是true
// Thread.currentThread().interrupt();
selfInterrupt();
}

3?Condition的awaitNanos&signalAll方法分析

awaitNanos:僅僅是在await方法的基礎上,做了一內內的改變,整體的邏輯思想都是一樣的。
掛起線程時,傳入要阻塞的時間,時間到了,自動喚醒,走添加到AQS隊列的邏輯

// await指定時間,多了個時間到了自動醒。
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// deadline:當前線程最多掛起到什么時間點final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// nanosTimeout的時間小于等于0,直接告辭!!
if (nanosTimeout <= 0L) {
// 正常扔到AQS隊列
transferAfterCancelledWait(node);
break;
}
// nanosTimeout的時間大于1000納秒時,才可以掛起線程
if (nanosTimeout >= spinForTimeoutThreshold)
// 如果大于,正常掛起
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
// 計算剩余的掛起時間,可能需要重新的走while循環,再次掛起線程
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
// 剩余的掛起時間
return deadline - System.nanoTime();
}

signalAll方法。這個方法一看就懂,之前signal是喚醒1個,這個是全部喚醒

// 以do-while的形式,將Condition單向鏈表中的所有Node,全部喚醒并扔到AQS隊列
private void doSignalAll(Node first) {
// 將頭尾都置位null~
lastWaiter = firstWaiter = null;
do {
// 拿到next節點的引用
Node next = first.nextWaiter;
// 斷開當前Node的nextWaiter
first.nextWaiter = null;
// 修改Node狀態,扔AQS隊列,是否喚醒!
transferForSignal(first);
// 指向下一個節點
first = next;
} while (first != null);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161825.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161825.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161825.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Centos設置nginx開機自啟動設置

Centos設置nginx開機自啟動設置 要設置CentOS中的Nginx開機自啟動&#xff0c;可以按照以下步驟進行操作&#xff1a; 首先&#xff0c;登錄到CentOS服務器上&#xff0c;并以root用戶或具有sudo權限的用戶身份執行以下命令來安裝Nginx&#xff08;如果尚未安裝&#xff09;&a…

字符集合!!!

字符集合&#xff01;&#xff01;&#xff01; 描述 輸入一個字符串&#xff0c;求出該字符串包含的字符集合&#xff0c;按照字母輸入的順序輸出。 數據范圍&#xff1a;輸入的字符串長度滿足 1 \le n \le 100 \1≤n≤100 &#xff0c;且只包含大小寫字母&#xff0c;區分大小…

中國信息通信研究院產業與規劃研究所校招一面、二面內容

本文介紹2024屆秋招中&#xff0c;中國信息通信研究院的數字孿生智慧城市研究員崗位一面、二面的面試基本情況、提問問題等。 10月投遞了中國信息通信研究院的數字孿生智慧城市研究員崗位&#xff0c;所在部門為數字孿生與城市數字化研究部。目前完成了一面與二面&#xff0c;在…

Django 模型和Admin站點管理(三)

一、定義模型 &#xff08;1&#xff09; 創建模型類&#xff0c;必須要繼承自 models.Model from django.db import models# Create your models here. #設計數據庫 #創建模型 class UserModel(models.Model):namemodels.CharField(max_length30) #對應于SQL name varchar(30…

K8s實戰RestartPoliy策略

一、默認策略為Always cmd.yaml apiVersion: v1 kind: Pod metadata:name: myapp-pod labels:app: myapp spec: containers:- name: myapp-container image: busyboxcommand: [sh, -c, echo OK!&& sleep 60]首先我們根據這個yaml創建一個測試的pod 執行命令 kubec…

Vue.observable可以在vue2中給新增的屬性增加響應式

將data中的config數據轉為響應式&#xff1a; data() {return {config: {password1: "YQd^7D1",password2: "YQd^7D2",password3: "YQd^7D3"}}; }, computed: {transformedConfig() {if (this.config) {return Object.keys(this.config).map(k…

C++二維數組中的查找

4. 二維數組中的查找 題目鏈接 牛客網 題目描述 給定一個二維數組,其每一行從左到右遞增排序,從上到下也是遞增排序。給定一個數,判斷這個數是否在該二維數組中。 Consider the following matrix: [[1, 4, 7, 11, 15],[2, 5, 8, 12, 19],[3, 6, 9, 16, 22],[1…

深度之眼Paper帶讀筆記GNN.08.GCN(下)

文章目錄 前言細節四&#xff1a;卷積核介紹圖卷積核初代目圖卷積核二代目契比雪夫多項式例子小結 GCN公式推導 實驗設置和結果分析數據集節點分類任務消息傳遞方式比較運行效率 總結關鍵點創新點啟發點 代碼復現train.pyutil.pymodel.pylayer.py 作業 前言 本課程來自深度之眼…

基于單片機直流電機調速(proteus仿真+源程序)

一、系統方案 1、本設計采用這51單片機作為主控器。 2、轉速值送到液晶1602顯示。 3、按鍵設加減速&#xff0c;開始暫停、正反轉。 二、硬件設計 原理圖如下&#xff1a; 三、單片機軟件設計 1、首先是系統初始化 en0; rw0; write_com(0x01); //lcd初始化 write_com(0x38)…

CQ 社區版 V2.6.0 發布 | SQL閃回、權限看板、新增數據源人大金倉等

前言 HELLO&#xff0c;大家好&#xff0c;又到了 CloudQuery 社區版發版時間&#xff01;本次更新版本為 v2.6.0&#xff0c;亮點多多&#xff0c;我們直入主題一起來看&#xff01; 一、本期亮點 新增 3 種數據源支持 V2.6.0&#xff0c;新增三種國產數據源支持&#xff…

cocos2dx ??Animate3D (一)

3D相關的動畫都是繼承Grid3DAction 本質上是用GirdBase進行創建動畫的小塊。 Shaky3D 晃動特效 // 持續時間(時間過后不會回到原來的樣子) // 整個屏幕被分成幾行幾列 // 晃動的范圍 // z軸是否晃動 static Shaky3D* create(float initWithDuration, const Size& …

內存可見性與指令重排序

文章目錄 內存可見性內存可見性問題代碼演示JMM&#xff08;Java Memory Model&#xff09; 指令重排序指令重排序問題代碼演示指令重排序分析 volatile關鍵字volatile 保證內存可見性 & 禁止指令重排序volatile 不保證原子性 在上一節介紹線程安全問題的過程中&#xff0c…

2023亞太杯數學建模B題思路 - 玻璃溫室中的微氣候法規

# 1 賽題 問題B 玻璃溫室中的微氣候法規 溫室作物的產量受到各種氣候因素的影響&#xff0c;包括溫度、濕度和風速[1]。其中&#xff0c;適 宜的溫度和風速是植物生長[2]的關鍵。為了調節玻璃溫室內的溫度、風速等氣候因素 , 溫室的設計通常采用帶有溫室風扇的通風系統&#x…

實驗4.數據全量、增量、比較更新

【實驗目的】 1.利用Kettle的“表輸入”&#xff0c;“表輸入出”&#xff0c;”JavaScript代碼”組件&#xff0c;實現數據全量更新。 2.熟練掌握“JavaScript代碼”&#xff0c;“表輸入”&#xff0c;“表輸入出”組件的使用&#xff0c;實現數據全量更新。 【實驗原理】 …

MATLAB算法實戰應用案例精講-【圖像處理】圖像縮放

目錄 前言 知識儲備 MATLAB圖像處理函數 數字數字圖像增強 數字數字圖像的變換

二級指針

*代表指針變量。int*為p的類型。故pp第一個*表示pp為指針int** pp&#xff0c;指向p的二級指針。 p中儲存a的地址&#xff0c;pp中儲存p的地址。 打印&#xff0c;printf中**pp的表示&#xff1a;pp中儲存的是p的地址&#xff0c;第一個*解引用地址p表示p的內容&#xff0c;p的…

Pickcode:教孩子們編碼的新視覺語言

Pickcode 通過視覺課程、聊天機器人、游戲和繪圖來教授編程。 Pickcode 是一種新的語言和編輯器&#xff0c;可以直觀地指導用戶編寫代碼來制作聊天機器人、動畫圖畫和游戲。Pickcode 旨在讓用戶在學習更高級的語言之前能夠充滿信心地開始學習編碼。 Pickcode 可視化編程語言…

回歸算法優化過程推導

假設存在一個數據集&#xff0c;包含工資、年齡及貸款額度三個維度的數據。我們需要根據這個數據集進行建模&#xff0c;從而在給定工資和年齡的情況下&#xff0c;實現對貸款額度的預測。其中&#xff0c;工資和年齡是模型構建時的兩個特征&#xff0c;額度是模型輸出的目標值…

被DDOS了怎么辦 要如何應對

DDoS攻擊的特點和類型 1. 特點 DDoS攻擊的特點是通過大量合法的請求或者無效的請求&#xff0c;消耗目標服務器的網絡帶寬和系統資源&#xff0c;使其無法正常運行。攻擊者通常使用多個主機發起攻擊&#xff0c;以達到更高的攻擊效果。 2. 常見類型 &#xff08;1&#xff09;S…

SPASS-ARIMA模型

基本概念 在預測中,對于平穩的時間序列,可用自回歸移動平均(AutoRegres- sive Moving Average, ARMA)模型及特殊情況的自回歸(AutoRegressive, AR)模型、移動平均(Moving Average, MA)模型等來擬合,預測該時間序列的未來值,但在實際的經濟預測中,隨機數據序列往往…