AQS獨占模式——資源獲取和釋放源碼分析

AQS資源獲取(獨占模式)

Node節點類

static final class Node {//標記當前節點的線程在共享模式下等待。static final Node SHARED = new Node();//標記當前節點的線程在獨占模式下等待。static final Node EXCLUSIVE = null;//waitStatus的值,表示當前節點的線程已取消(等待超時或被中斷)static final int CANCELLED =  1;//waitStatus的值,表示后繼節點的線程需要被喚醒static final int SIGNAL    = -1;//waitStatus的值,表示當前節點在等待某個條件,正處于condition等待隊列中static final int CONDITION = -2;//waitStatus的值,表示在當前有資源可用,能夠執行后續的acquireShared操作static final int PROPAGATE = -3;//等待狀態,值如上,1、-1、-2、-3。volatile int waitStatus;//前趨節點volatile Node prev;//后繼節點volatile Node next;//當前線程volatile Thread thread;//等待隊列中的后繼節點,共享模式下值為SHARED常量Node nextWaiter;//判斷共享模式的方法final boolean isShared() {return nextWaiter == SHARED;}//返回前趨節點,沒有報NPEfinal Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}//下面是三個構造方法Node() {}    // Used to establish initial head or SHARED markeNode(Thread thread, Node mode) {     // Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;}
}

嘗試獲取資源,方法分析

    public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

獲取失敗調用addWaiter將當前線程封裝成獨占模式的節點,添加到AQS隊列尾部

	// mode 獨占模式 共享模式private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// 獲取到尾節點Node pred = tail;// 尾節點不為空if (pred != null) {// 新節點,跟在尾節點后,新節點的前驅指向獲取到的尾節點node.prev = pred;// 新節點設置為尾節點if (compareAndSetTail(pred, node)) {// 剛剛獲取的尾節點的后繼節點指向新的節點,新節點成為最終尾節點,添加到隊列尾部pred.next = node;return node;}}// 隊列沒有節點,直接加入隊列enq(node);return node;}// 入隊方法private Node enq(final Node node) {// 自旋for (;;) {// 獲取尾節點Node t = tail;// 為空,隊列為空,直接隊尾為同一個節點,入隊if (t == null) { if (compareAndSetHead(new Node()))tail = head;} else {// 不為空,新節點的前驅為隊列的尾節點node.prev = t;// 新節點成為隊列尾節點if (compareAndSetTail(t, node)) {// 舊的尾節點的后繼是新節點,新節點成為隊列新的尾節點t.next = node;return t;}}}}

通過addWaiter已經將當前線程封裝成獨占模式的 Node 節點,并成功放入隊列尾部。接下來會調用acquireQueued方法在等待隊列中排隊

final boolean acquireQueued(final Node node, int arg) {
// 獲取資源失敗標識boolean failed = true;try {// 線程是否被中斷標識boolean interrupted = false;// 自旋 掛起for (;;) {// 前驅節點final Node p = node.predecessor();// 是否頭節點,再次獲取鎖成功if (p == head && tryAcquire(arg)) {// 當前節點設為頭節點setHead(node);// 斷掉引用p.next = null; // help GC 頭節點出列failed = false;return interrupted;}// 如果不是頭節點或獲取鎖失敗 準備阻塞if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)// 取消同步狀態cancelAcquire(node);}}//將當前節點設置為頭節點
private void setHead(Node node) {head = node;node.thread = null;node.prev = null;
}
// 判斷當前線程是否可以進入waiting狀態
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 獲取前驅節點的等待狀態int ws = pred.waitStatus;if (ws == Node.SIGNAL) // 可以被喚醒return true;if (ws > 0) { // 表示當前線程被取消do {// 關鍵  節點一直往前移動,直到找到狀態<=0的節點node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {// 下面節點進來的條件,前驅節點是SIGNAL,這里設置為SIGNALcompareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}// 掛起線程private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();
}protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();// 判斷是否0int c = getState();if (c == 0) {if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {// 設為當前線程setExclusiveOwnerThread(current);return true;}}// 不為0 嘗試獲取鎖else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}

獲取資源的整體流程圖如下:

AQS資源獲取(獨占模式)特點

1.互斥訪問(Mutual Exclusion)

  • 單線程持有:同一時間只允許一個線程持有資源
  • 狀態管理:通過 state 變量(volatile int)表示資源狀態
  • CAS操作:使用 compareAndSetState 確保狀態更新原子性
  • 示例:ReentrantLock 中 state=0 表示未鎖定,state>0 表示鎖定狀態

2. 線程阻塞隊列(CLH Queue)

  • FIFO隊列:使用雙向鏈表實現的 CLH 變體隊列
  • 節點類型:Node.EXCLUSIVE 表示獨占模式節點
  • 排隊機制:獲取資源失敗的線程會被封裝為節點加入隊列尾部

3. 可重入支持(Reentrancy)

  • 重入計數:state 變量記錄重入次數
  • 持有線程:通過 exclusiveOwnerThread 記錄當前持有線程
  • 示例:ReentrantLock 允許線程多次獲取同一把鎖

在這里插入圖片描述

AQS資源釋放(獨占模式)

    public void unlock() {sync.release(1);}public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)// 喚醒后繼節點unparkSuccessor(h);return true;}return false;}// 喚醒后繼節點的線程,傳入節點private void unparkSuccessor(Node node) {// 獲取當前節點的等待狀態int ws = node.waitStatus;if (ws < 0)// <0 嘗試設置為0node.compareAndSetWaitStatus(ws, 0);// 獲取節點后繼Node s = node.next;// 后繼節點為空或等待狀態>0 節點取消if (s == null || s.waitStatus > 0) {s = null;// 從尾部向前遍歷for (Node p = tail; p != node && p != null; p = p.prev)if (p.waitStatus <= 0)s = p;}// 不為空,準備進行喚醒操作if (s != null)// 線程停止阻塞LockSupport.unpark(s.thread);}

AQS 資源釋放(獨占模式)流程圖
在這里插入圖片描述

AQS資源釋放(獨占模式)特點

1.狀態更新:

  • 通過 tryRelease 更新同步狀態
  • 清除當前持有線程

2.喚醒策略:

  • 只喚醒頭節點的下一個有效節點
  • 采用從后向前查找策略解決并發入隊問題

3.線程安全:

  • 使用 CAS 更新 waitStatus
  • 無鎖化設計確保高性能

4.取消處理:

  • 自動跳過已取消節點(waitStatus > 0)
  • 確保喚醒的節點都是有效等待節點

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

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

相關文章

壓測過程中TPS上不去可能是什么原因

進行性能分析 接口沒有報錯或者錯誤率低于1%&#xff0c;繼續增加并發還是一樣&#xff0c;這個時候需要考慮幾點 1.是否觸發限流&#xff0c;比如waf、Nginx等情況&#xff0c;有沒有一些限流的情況&#xff0c;如果觸發了限流&#xff0c;請求是沒有達到后端的&#xff0c;所…

Golang 解大整數乘法

文章目錄 Golang 解大整數乘法問題描述&#xff1a;LeetCode 43. 字符串相乘思路Golang 代碼 Golang 解大整數乘法 在初學 C 語言的時候&#xff0c;我們一定接觸過“字符串相加”或“字符串相乘”之類的問題&#xff0c;對于初學者而言&#xff0c;這類問題的難度一般來說是比…

web3-區塊鏈的技術安全/經濟安全以及去杠桿螺旋(經濟穩定)

web3-區塊鏈的技術安全/經濟安全以及去杠桿螺旋&#xff08;經濟穩定&#xff09; 三個基本設計問題 技術安全 在技術結構中對其進行原子級的、瞬時利用&#xff08;無風險&#xff09; 無風險&#xff0c;因為攻擊者的結果還是二進制的&#xff1a; 只會是攻擊成功 獲利或…

Java多線程通信:wait/notify與sleep的深度剖析(時序圖詳解)

在Java多線程編程中&#xff0c;線程間的通信與協作是實現復雜并發邏輯的關鍵。wait()、notify()以及sleep()方法作為線程控制的重要工具&#xff0c;有著各自獨特的使用場景與規則。本文將深入探討wait()和notify()的協作機制&#xff0c;以及sleep()的阻塞特性&#xff0c;同…

關于使用EasyExcel、 Vue3實現導入導出功能

后端部分: 其中查詢數據的服務省略 1、引用 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version></dependency> 2、controller package com.rs.cphs.sys.controller;i…

機器學習中的數據準備關鍵技術

有效的數據準備對于構建強大的機器學習模型至關重要。本文檔總結并闡述了為監督和非監督學習任務準備數據的關鍵技術。 1. 理解數據類型 有兩種數據類型。定性數據描述對象的特征&#xff0c;而定量數據描述對象的數量。 定性&#xff08;分類&#xff09;數據 名義&#x…

深度學習——基于卷積神經網絡實現食物圖像分類【3】(保存最優模型)

文章目錄 引言一、項目概述二、環境配置三、數據預處理3.1 數據轉換設置3.2 數據集準備 四、自定義數據集類五、CNN模型架構六、訓練與評估流程6.1 訓練函數6.2 評估與模型保存 七、完整訓練流程八、模型保存與加載8.1 保存模型8.2 加載模型 九、優化建議十、常見問題解決十一、…

《棒球百科》棒球怎么玩·棒球9號位

用最簡單的方式介紹棒球的核心玩法和規則&#xff0c;完全零基礎也能看懂&#xff1a; 一句話目標 進攻方&#xff1a;用球棒把球打飛&#xff0c;然后拼命跑完4個壘包&#xff08;逆時針繞一圈&#xff09;得分。 防守方&#xff1a;想盡辦法讓進攻方出局&#xff0c;阻止他…

語言模型是怎么工作的?通俗版原理解讀!

大模型為什么能聊天、寫代碼、懂醫學&#xff1f; 我們從四個關鍵模塊&#xff0c;一步步拆開講清楚 &#x1f447; ? 模塊一&#xff1a;模型的“本事”從哪來&#xff1f;靠訓練數據 別幻想它有意識&#xff0c;它的能力&#xff0c;全是“喂”出來的&#xff1a; 吃過成千…

nrf52811墨水屏edp_service.c文件學習

on_connect函數 /**brief Function for handling the ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.** param[in] p_epd EPD Service structure.* param[in] p_ble_evt Pointer to the event received from BLE stack.*/ static void on_connect(ble_epd_t …

Nginx-2 詳解處理 Http 請求

Nginx-2 詳解處理 Http 請求 Nginx 作為當今最流行的開源 Web 服務器之一&#xff0c;以其高性能、高穩定性和豐富的功能而聞名。在處理 HTTP請求 的過程中&#xff0c;Nginx 采用了模塊化的設計&#xff0c;將整個請求處理流程劃分為若干個階段&#xff0c;每個階段都可以由特…

40-Oracle 23 ai Bigfile~Smallfile-Basicfile~Securefile矩陣對比

小伙伴們是不是在文件選擇上還默認給建文件4G/個么&#xff0c;在oracle每個版本上系統默認屬性是什么&#xff0c;選擇困難癥了沒&#xff0c;一起一次性文件存儲和默認屬性看透。 基于Oracle歷代在存儲架構的技術演進分析&#xff0c;結合版本升級和23ai新特性&#xff0c;一…

【一】零基礎--分層強化學習概覽

分層強化學習&#xff08;Hierarchical Reinforcement Learning, HRL&#xff09;最早一般視為1993 年封建強化學習的提出. 一、HL的基礎理論 1.1 MDP MDP&#xff08;馬爾可夫決策過程&#xff09;&#xff1a;MDP是一種用于建模序列決策問題的框架&#xff0c;包含狀態&am…

Java延時

在 Java 中實現延時操作主要有以下幾種方式&#xff0c;根據使用場景選擇合適的方法&#xff1a; 1. Thread.sleep()&#xff08;最常用&#xff09; java 復制 下載 try {// 延時 1000 毫秒&#xff08;1秒&#xff09;Thread.sleep(1000); } catch (InterruptedExcepti…

電阻篇---下拉電阻的取值

下拉電阻的取值需要綜合考慮電路驅動能力、功耗、信號完整性、噪聲容限等多方面因素。以下是詳細的取值分析及方法&#xff1a; 一、下拉電阻的核心影響因素 1. 驅動能力與電流限制 單片機 IO 口驅動能力&#xff1a;如 STM32 的 IO 口在輸入模式下的漏電流通常很小&#xf…

NY271NY274美光科技固態NY278NY284

美光科技NY系列固態硬盤深度剖析&#xff1a;技術、市場與未來 技術前沿&#xff1a;232層NAND架構與性能突破 在存儲技術的賽道上&#xff0c;美光科技&#xff08;Micron&#xff09;始終是行業領跑者。其NY系列固態硬盤&#xff08;SSD&#xff09;憑借232層NAND閃存架構的…

微信開發者工具 插件未授權使用,user uni can not visit app

參考&#xff1a;https://www.jingpinma.cn/archives/159.html 問題描述 我下載了一個別人的小程序&#xff0c;想運行看看效果&#xff0c;結果報錯信息如下 原因 其實就是插件沒有安裝&#xff0c;需要到小程序平臺安裝插件。處理辦法如下 在 app.json 里&#xff0c;聲…

UE5 讀取配置文件

使用免費的Varest插件&#xff0c;可以讀取本地的json數據 獲取配置文件路徑&#xff1a;當前配置文件在工程根目錄&#xff0c;打包后在 Windows/項目名稱 下 讀取json 打包后需要手動復制配置文件到Windows/項目名稱 下

【kdump專欄】KEXEC機制中SME(安全內存加密)

【kdump專欄】KEXEC機制中SME&#xff08;安全內存加密&#xff09; 原始代碼&#xff1a; /* Ensure that these pages are decrypted if SME is enabled. */ 533 if (pages) 534 arch_kexec_post_alloc_pages(page_address(pages), 1 << order, 0);&#x1f4cc…

C# vs2022 找不到指定的 SDK“Microsof.NET.Sdk

找不到指定的 SDK"Microsof.NET.Sdk 第一查 看 系統盤目錄 C:\Program Files\dotnet第二 命令行輸入 dotnet --version第三 檢查環境變量總結 只要執行dotnet --version 正常返回版本號此問題即解決 第一查 看 系統盤目錄 C:\Program Files\dotnet 有2種方式 去檢查 是否…