最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的AQS入門攻略

 更新時(shí)間:2025年10月22日 11:17:26   作者:程序員小假  
AQS(AbstractQueuedSynchronizer)是JUC包中用于構(gòu)建鎖和同步器的核心框架,它通過volatile的state變量和FIFO隊(duì)列實(shí)現(xiàn)線程同步,簡(jiǎn)化了同步工具的開發(fā),并支持獨(dú)占與共享兩種模式,本文介紹Java中的AQS是什么,感興趣的朋友跟隨小編一起看看吧

一、AQS 是什么?

AQS,全稱 AbstractQueuedSynchronizer,即抽象隊(duì)列同步器。

  • 抽象:它是一個(gè)抽象類,本身不能直接實(shí)例化,需要子類去繼承它,并實(shí)現(xiàn)其保護(hù)方法來管理同步狀態(tài)。
  • 隊(duì)列:它內(nèi)部維護(hù)了一個(gè)先進(jìn)先出(FIFO)的等待隊(duì)列,用于存放那些沒有搶到鎖的線程。
  • 同步器:它是構(gòu)建鎖和其他同步組件(如 Semaphore、CountDownLatch 等)的基礎(chǔ)框架。

核心思想:
AQS 使用一個(gè)整型的 volatile 變量(state 來表示同步狀態(tài)(例如,鎖被重入的次數(shù)、許可的數(shù)量等),并通過一個(gè)內(nèi)置的 FIFO 隊(duì)列來完成資源獲取線程的排隊(duì)工作。

設(shè)計(jì)模式:
AQS 是 模板方法模式 的經(jīng)典應(yīng)用。父類(AQS)定義了骨架和核心算法,而將一些關(guān)鍵的操作以 protected 方法的形式留給子類去實(shí)現(xiàn)。這樣,實(shí)現(xiàn)一個(gè)自定義同步器只需要關(guān)注如何管理 state 狀態(tài)即可,至于線程的排隊(duì)、等待、喚醒等復(fù)雜操作,AQS 已經(jīng)幫我們完成了。

二、AQS 的核心結(jié)構(gòu)

AQS 的核心可以概括為三部分:同步狀態(tài)(state)、等待隊(duì)列 和 條件隊(duì)列。

1. 同步狀態(tài)(State)

這是一個(gè) volatile int 類型的變量,是 AQS 的靈魂。

private volatile int state;

它的具體含義由子類決定,非常靈活:

  • 在 ReentrantLock 中,state 表示鎖被同一個(gè)線程重復(fù)獲取的次數(shù)。state=0 表示鎖空閑,state=1 表示鎖被占用,state>1 表示鎖被重入。
  • 在 Semaphore 中,state 表示當(dāng)前可用的許可數(shù)量。
  • 在 CountDownLatch 中,state 表示計(jì)數(shù)器當(dāng)前的值。

對(duì) state 的操作是原子的,通過 getState()setState(int newState)compareAndSetState(int expect, int update) 等方法進(jìn)行。

2. 等待隊(duì)列(CLH 隊(duì)列的變體)

這是一個(gè)雙向鏈表,是 AQS 實(shí)現(xiàn)阻塞鎖的關(guān)鍵。當(dāng)線程請(qǐng)求共享資源失敗時(shí),AQS 會(huì)將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成一個(gè)節(jié)點(diǎn)(Node) 并將其加入隊(duì)列的尾部,同時(shí)阻塞該線程。

  • 頭節(jié)點(diǎn)(Head):指向獲取到資源的線程所在的節(jié)點(diǎn)。頭節(jié)點(diǎn)不持有線程,是一個(gè)“虛節(jié)點(diǎn)”。
  • 尾節(jié)點(diǎn)(Tail):指向隊(duì)列中最后一個(gè)節(jié)點(diǎn)。

當(dāng)一個(gè)線程釋放資源時(shí),它會(huì)喚醒后繼節(jié)點(diǎn),后繼節(jié)點(diǎn)成功獲取資源后,會(huì)將自己設(shè)置為新的頭節(jié)點(diǎn)。

主要原理圖如下:

AQS 使用一個(gè) Volatile 的 int 類型的成員變量來表示同步狀態(tài),通過內(nèi)置的 FIFO 隊(duì)列來完成資源獲取的排隊(duì)工作,通過 CAS 完成對(duì) State 值的修改。

3. 條件隊(duì)列(Condition Object)

AQS 內(nèi)部類 ConditionObject 實(shí)現(xiàn)了 Condition 接口,用于支持 await/signal 模式的線程間協(xié)作。每個(gè) ConditionObject 對(duì)象都維護(hù)了一個(gè)自己的單向鏈表(條件隊(duì)列)。

  • 當(dāng)線程調(diào)用 Condition.await() 時(shí),會(huì)釋放鎖,并將當(dāng)前線程構(gòu)造成節(jié)點(diǎn)加入條件隊(duì)列,然后阻塞。
  • 當(dāng)線程調(diào)用 Condition.signal() 時(shí),會(huì)將條件隊(duì)列中的第一個(gè)等待節(jié)點(diǎn)轉(zhuǎn)移到 AQS 的等待隊(duì)列中,等待重新獲取鎖。

注意:一個(gè) AQS 實(shí)例可以對(duì)應(yīng)多個(gè) Condition 對(duì)象(即多個(gè)條件隊(duì)列),但只有一個(gè)等待隊(duì)列。

三、AQS 的設(shè)計(jì)與關(guān)鍵方法

AQS 將資源獲取的方式分為兩種:

  • 獨(dú)占模式(Exclusive):一次只有一個(gè)線程能執(zhí)行,如 ReentrantLock。
  • 共享模式(Shared):多個(gè)線程可以同時(shí)執(zhí)行,如 Semaphore、CountDownLatch。

AQS 提供了頂層的入隊(duì)和出隊(duì)邏輯,而將嘗試獲取資源嘗試釋放資源的具體策略留給了子類。

需要子類重寫的關(guān)鍵方法(Protected)

這些方法在 AQS 中是 protected 的,默認(rèn)拋出 UnsupportedOperationException

獨(dú)占模式:

  • boolean tryAcquire(int arg):嘗試以獨(dú)占方式獲取資源。成功返回 true,失敗返回 false。
  • boolean tryRelease(int arg):嘗試以獨(dú)占方式釋放資源。成功返回 true,失敗返回 false。

共享模式:

  • int tryAcquireShared(int arg):嘗試以共享方式獲取資源。負(fù)數(shù)表示失敗;0 表示成功,但后續(xù)共享獲取可能失??;正數(shù)表示成功,且后續(xù)共享獲取可能成功。
  • boolean tryReleaseShared(int arg):嘗試以共享方式釋放資源。

其他:

  • boolean isHeldExclusively():當(dāng)前同步器是否在獨(dú)占模式下被線程占用。在 Condition 相關(guān)操作中會(huì)用到。

供外部調(diào)用的重要方法(Public)

這些是模板方法,子類一般不重寫,使用者(或子類)直接調(diào)用。

獨(dú)占模式:

  • void acquire(int arg):以獨(dú)占模式獲取資源,忽略中斷。如果獲取失敗,會(huì)進(jìn)入等待隊(duì)列。
  • void acquireInterruptibly(int arg):同上,但響應(yīng)中斷。
  • boolean tryAcquireNanos(int arg, long nanosTimeout):在 acquireInterruptibly 基礎(chǔ)上增加了超時(shí)限制。
  • boolean release(int arg):以獨(dú)占模式釋放資源。

共享模式:

  • void acquireShared(int arg):以共享模式獲取資源。
  • void acquireSharedInterruptibly(int arg):響應(yīng)中斷的共享獲取。
  • boolean tryAcquireSharedNanos(int arg, long nanosTimeout):帶超時(shí)的共享獲取。
  • boolean releaseShared(int arg):以共享模式釋放資源。

四、源碼級(jí)工作流程解析(以acquire為例)

我們來看一下最核心的 acquire 方法,它展示了 AQS 的完整工作流程:

public final void acquire(int arg) {
    if (!tryAcquire(arg) && // 1. 嘗試直接獲取資源(子類實(shí)現(xiàn))
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 獲取失敗,則加入隊(duì)列;3. 在隊(duì)列中自旋/阻塞等待
        selfInterrupt(); // 如果在等待過程中被中斷,補(bǔ)上中斷標(biāo)記
}
  • tryAcquire(arg)
    • 這是子類實(shí)現(xiàn)的方法。比如在 ReentrantLock 的非公平鎖實(shí)現(xiàn)中,它會(huì)直接嘗試使用 CAS 修改 state,如果成功,就將當(dāng)前線程設(shè)置為獨(dú)占線程。
    • 如果 tryAcquire 成功,整個(gè) acquire 方法就結(jié)束了,線程繼續(xù)執(zhí)行。
    • 如果失敗,進(jìn)入下一步。
  • addWaiter(Node.EXCLUSIVE)
    • 創(chuàng)建一個(gè)代表當(dāng)前線程的 Node 節(jié)點(diǎn),模式為獨(dú)占模式(Node.EXCLUSIVE)。
    • 通過 CAS 操作,快速地將這個(gè)新節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn)。如果失敗,則進(jìn)入 enq(node) 方法,通過自旋 CAS 的方式確保節(jié)點(diǎn)被成功添加到隊(duì)列尾部。
  • acquireQueued(final Node node, int arg)
    • 這是核心中的核心。節(jié)點(diǎn)入隊(duì)后,會(huì)在這個(gè)方法里進(jìn)行自旋(循環(huán))等待。
    • 在循環(huán)中,它會(huì)檢查自己的前驅(qū)節(jié)點(diǎn)是不是頭節(jié)點(diǎn)(p == head)。如果是,說明自己是隊(duì)列中第一個(gè)等待的線程,會(huì)再次調(diào)用 tryAcquire 嘗試獲取資源(因?yàn)榇藭r(shí)鎖可能剛好被釋放了,這是一個(gè)避免不必要的線程掛起、提高性能的優(yōu)化)。
    • 如果獲取成功,就將自己設(shè)為新的頭節(jié)點(diǎn),然后返回。
    • 如果前驅(qū)不是頭節(jié)點(diǎn),或者再次嘗試獲取失敗,則會(huì)調(diào)用 shouldParkAfterFailedAcquire 方法,檢查并更新前驅(qū)節(jié)點(diǎn)的狀態(tài)(比如將其 waitStatus 設(shè)置為 SIGNAL,表示“當(dāng)你釋放鎖時(shí),需要喚醒我”)。
    • 如果一切就緒,就調(diào)用 parkAndCheckInterrupt() 方法,使用 LockSupport.park(this) 阻塞(掛起)當(dāng)前線程
    • 當(dāng)線程被喚醒后(通常是由前驅(qū)節(jié)點(diǎn)釋放鎖時(shí) unpark 的),會(huì)再次檢查自己是否是頭節(jié)點(diǎn)的后繼,并重復(fù)上述自旋過程,直到成功獲取資源。
  • selfInterrupt()
    • 如果在等待過程中線程被中斷,acquireQueued 方法會(huì)返回 true,這里會(huì)調(diào)用 selfInterrupt 補(bǔ)上中斷標(biāo)志,因?yàn)?AQS 在 acquire 過程中是忽略中斷的。

釋放流程(release)相對(duì)簡(jiǎn)單:

public final boolean release(int arg) {
    if (tryRelease(arg)) { // 1. 子類嘗試釋放資源
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 2. 喚醒后繼節(jié)點(diǎn)
        return true;
    }
    return false;
}

unparkSuccessor 會(huì)找到隊(duì)列中第一個(gè)需要喚醒的線程(通常是頭節(jié)點(diǎn)的下一個(gè)有效節(jié)點(diǎn)),然后調(diào)用 LockSupport.unpark(s.thread) 將其喚醒。

五、AQS 的應(yīng)用舉例

AQS 是 JUC 包的基石,幾乎所有的同步工具都基于它:

  • ReentrantLock:使用 AQS 的獨(dú)占模式,state 表示重入次數(shù)。
  • ReentrantReadWriteLock:讀寫鎖。AQS 的 state 高16位表示讀鎖狀態(tài),低16位表示寫鎖狀態(tài)。
  • Semaphore:使用 AQS 的共享模式,state 表示可用許可數(shù)。
  • CountDownLatch:使用 AQS 的共享模式,state 表示計(jì)數(shù)器值。countDown() 是 releaseShared,await() 是 acquireShared。
  • ThreadPoolExecutor:其內(nèi)部的工作線程 Worker 類,也繼承了 AQS,用于實(shí)現(xiàn)獨(dú)占鎖,來判斷線程是否空閑。

六、總結(jié)

AQS 的核心貢獻(xiàn)在于,它提供了一個(gè)強(qiáng)大的框架,將復(fù)雜的線程排隊(duì)、阻塞、喚醒等底層操作封裝起來,讓同步器的開發(fā)者只需要關(guān)注一個(gè)核心問題:如何管理那個(gè) state 變量。

它的優(yōu)點(diǎn):

  • 極大地降低了構(gòu)建鎖和同步器的復(fù)雜度。
  • 性能高效:通過自旋、CAS 等無鎖編程技術(shù),減少了線程上下文切換的開銷。
  • 靈活強(qiáng)大:通過兩種模式的區(qū)分,可以構(gòu)建出各種復(fù)雜的同步工具。

到此這篇關(guān)于Java中的AQS入門攻略的文章就介紹到這了,更多相關(guān)java aqs是什么內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 線程池_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java 線程池_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    系統(tǒng)啟動(dòng)一個(gè)新線程的成本是比較高的,因?yàn)樗婕暗脚c操作系統(tǒng)的交互。在這種情況下,使用線程池可以很好的提供性能,尤其是當(dāng)程序中需要?jiǎng)?chuàng)建大量生存期很短暫的線程時(shí),更應(yīng)該考慮使用線程池
    2017-05-05
  • Springboot引入多個(gè)yml方法(多種方案)

    Springboot引入多個(gè)yml方法(多種方案)

    SpringBoot默認(rèn)加載的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活該文件這篇文章主要介紹了Springboot引入多個(gè)yml方法,需要的朋友可以參考下
    2019-10-10
  • IDEA中用maven連接數(shù)據(jù)庫(kù)的教程

    IDEA中用maven連接數(shù)據(jù)庫(kù)的教程

    這篇文章主要介紹了IDEA中用maven連接數(shù)據(jù)庫(kù)的教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • java??常見位邏輯運(yùn)算符梳理

    java??常見位邏輯運(yùn)算符梳理

    這篇文章主要介紹了java常見位邏輯運(yùn)算符梳理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參一下下面文章詳細(xì)內(nèi)容
    2022-08-08
  • 深入了解Java SpringBoot自動(dòng)裝配原理

    深入了解Java SpringBoot自動(dòng)裝配原理

    在使用springboot時(shí),很多配置我們都沒有做,都是springboot在幫我們完成,這很大一部分歸功于springboot自動(dòng)裝配。本文將詳細(xì)為大家講解SpringBoot的自動(dòng)裝配原理,需要的可以參考一下
    2022-03-03
  • Java Volatile關(guān)鍵字同步機(jī)制詳解

    Java Volatile關(guān)鍵字同步機(jī)制詳解

    這篇文章主要介紹了Java Volatile關(guān)鍵字同步機(jī)制詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 詳解Java線程-守護(hù)線程與用戶線程

    詳解Java線程-守護(hù)線程與用戶線程

    這篇文章主要介紹了Java守護(hù)線程與用戶線程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Hibernate命名策略詳解

    Hibernate命名策略詳解

    本文主要介紹了Hibernate命名策略。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-01-01
  • Java中RocketMQ的流量削峰詳解

    Java中RocketMQ的流量削峰詳解

    這篇文章主要介紹了Java中RocketMQ的流量削峰詳解,MQ的主要特點(diǎn)為解耦、異步、削峰,該文章主要記錄與分享個(gè)人在實(shí)際項(xiàng)目中的RocketMQ削峰用法,用于減少數(shù)據(jù)庫(kù)壓力的業(yè)務(wù)場(chǎng)景,需要的朋友可以參考下
    2023-09-09
  • 一篇文章弄懂Mybatis中#和$的區(qū)別

    一篇文章弄懂Mybatis中#和$的區(qū)別

    mybatis為我們提供了兩種支持動(dòng)態(tài)sql的語法#{}以及${},兩者都是動(dòng)態(tài)的向sql語句中傳入需要的參數(shù),下面這篇文章主要給大家介紹了如何通過一篇文章弄懂Mybatis中#和$區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2021-12-12

最新評(píng)論

连江县| 泗洪县| 拉孜县| 宾阳县| 楚雄市| 鹤岗市| 峨山| 郧西县| 屏南县| 札达县| 筠连县| 铁力市| 阿荣旗| 呼玛县| 明水县| 波密县| 日土县| 阳朔县| 和政县| 荥经县| 刚察县| 隆化县| 宣化县| 临安市| 东乡县| 涿鹿县| 固安县| 策勒县| 贡觉县| 青田县| 惠州市| 耿马| 柘荣县| 唐山市| 麟游县| 阜平县| 万全县| 汶上县| 满城县| 萝北县| 工布江达县|