Java中的AQS同步隊(duì)列問(wèn)題詳解
AQS 同步隊(duì)列
1、AQS 介紹
AQS 是 AbstractQueuedSynchronizer 的縮寫(xiě),他是一個(gè)抽象同步類(lèi),為 JUC 包下的大多數(shù)同步工具提供了核心實(shí)現(xiàn),例如 ReentrantLock 的底層就是使用同步隊(duì)列。AQS 提供一套基礎(chǔ)的機(jī)制來(lái)實(shí)現(xiàn)線程的同步、阻塞與喚醒、等待隊(duì)列等功能,也就是想要深入學(xué)習(xí)線程工具類(lèi),這個(gè)同步隊(duì)列就必須得掌握。
1.1、類(lèi)圖關(guān)系
下面是整個(gè) AQS 的類(lèi)結(jié)構(gòu)實(shí)現(xiàn)(從源碼中直接打印的圖,平常學(xué)習(xí)源碼也可以這樣打印出來(lái)觀察整個(gè)程序的運(yùn)行情況,有助于理解),從圖中我們不難發(fā)現(xiàn) AQS 內(nèi)部持有兩個(gè) Node 類(lèi)型的 head 、tail 屬性,我們?cè)谑裁磿r(shí)候會(huì)接觸到頭尾節(jié)點(diǎn)的定義,大家都是有經(jīng)驗(yàn)的開(kāi)發(fā)人員,肯定都能想到是鏈表當(dāng)中。
在屬性列中我們可以看見(jiàn)一個(gè) state:int 這樣的一個(gè)狀態(tài)字段,Lock 的重入特性就是根據(jù)此來(lái)實(shí)現(xiàn)的,可以表示當(dāng)前線程的鎖重入次數(shù)。整個(gè)類(lèi)繼承自 AbstractOwnableSynchronizer 類(lèi),自然擁有對(duì)于其父類(lèi)中屬性的一些控制權(quán),而里面的 Thraed 的線程就是表示當(dāng)前持有鎖的線程,在整個(gè)鎖過(guò)程中具有很重要的地位。

1.2、節(jié)點(diǎn)剖析
當(dāng)然鏈表只是一種組織存儲(chǔ)形式的一種數(shù)據(jù)結(jié)構(gòu),這里叫做 FIFO 雙向隊(duì)列,至于為什么是雙向的呢,看一下 Node 的節(jié)點(diǎn)定義就能明白,一個(gè)節(jié)點(diǎn)中含有 prev 、next 節(jié)點(diǎn)來(lái)快速訪問(wèn)前驅(qū)和后繼節(jié)點(diǎn),不就是典型的雙向形式呢。
相信大家在看到這個(gè)類(lèi)字段的屬性名定義之后就能才出來(lái)其的作用,但是這里還是介紹一下主要的幾個(gè)字段含義,印證大家的猜想。
| 屬性 | 作用 |
|---|---|
| thread | 表示當(dāng)前節(jié)點(diǎn)封裝的具體線程 |
| SHARED | 表示當(dāng)前線程是獲取共享資源時(shí)被阻塞 |
| EXCLUSIVE | 表示當(dāng)前線程是獲取獨(dú)占資源時(shí)被掛起 |
| prev | 當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn) |
| next | 當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn) |
| waitStatus | 記錄當(dāng)前線程的等待狀態(tài),其狀態(tài)取值就是下面的四個(gè)字段 |
| CANCELLED | 取消線程 |
| SIGNAL | 線程需要被喚醒 |
| CONDITION | 線程在 condition 中等待 |
| PROPAGATE | 釋放共享資源時(shí)需要通知其余節(jié)點(diǎn)線程 |
2、AQS 實(shí)現(xiàn)原理
上面我們知道了 AQS 其實(shí)就是一個(gè)雙向的隊(duì)列,如下圖的結(jié)構(gòu)一樣。在線程獲取鎖失敗的情況下,會(huì)被封裝成一個(gè) Node 節(jié)點(diǎn)而插入到隊(duì)列當(dāng)中;當(dāng)其他的線程釋放鎖之后又會(huì)從隊(duì)列中喚醒一個(gè)節(jié)點(diǎn)去爭(zhēng)搶鎖。

2.1、隊(duì)列初始化
通過(guò)源碼我們可以發(fā)現(xiàn),在 AQS 進(jìn)行初始化的時(shí)候的并沒(méi)有對(duì) head 、tail 進(jìn)行初始化,而這兩個(gè)節(jié)點(diǎn)是控制整個(gè)隊(duì)列的,也就是說(shuō)一開(kāi)始整個(gè)隊(duì)列處于 null 狀態(tài)。
protected AbstractQueuedSynchronizer() {}當(dāng)?shù)谝粋€(gè)線程爭(zhēng)搶鎖失敗之后會(huì)封裝成 Node 進(jìn)入到同步隊(duì)列當(dāng)中,這個(gè)時(shí)候就會(huì)進(jìn)行判斷,如果當(dāng)前隊(duì)列為空就會(huì)進(jìn)行初始化(未進(jìn)行初始化),初始化完成之后就將當(dāng)前線程節(jié)點(diǎn)接在隊(duì)列的尾部。
private final void initializeSyncQueue() {
Node h;
if (HEAD.compareAndSet(this, (Void)null, h = new Node())) {
this.tail = h;
}
}
2.2、追加節(jié)點(diǎn)
追加節(jié)點(diǎn)的操作就是簡(jiǎn)單的鏈表尾部添加節(jié)點(diǎn)的過(guò)程了,這里就不做過(guò)多的贅述。這里來(lái)看看前面初始化時(shí)會(huì)添加一個(gè)額外的節(jié)點(diǎn)在隊(duì)列中,其實(shí)這個(gè)節(jié)點(diǎn)就是代表當(dāng)前已經(jīng)獲取了鎖的線程,至于為什么這么設(shè)置,大家往后看就明白了。

3、AQS 喚醒動(dòng)作
在進(jìn)行線程喚醒的過(guò)程中,會(huì)優(yōu)先喚醒當(dāng)前持有鎖線程的下一個(gè)節(jié)點(diǎn)線程。
- head 指針指向下一個(gè)節(jié)點(diǎn);
- 原來(lái)頭結(jié)點(diǎn)的 next 指向 null;
- 當(dāng)前頭結(jié)點(diǎn)的 prev 指向 null;
- 當(dāng)前頭結(jié)點(diǎn)的 thread 指向 null。
這樣就完成線程的喚醒操作了,但是這樣來(lái)講其實(shí)是不完美的,因?yàn)?AQS 只是一個(gè)抽象的統(tǒng)一工具,本身并沒(méi)有對(duì)業(yè)務(wù)進(jìn)行規(guī)范,還是要結(jié)合具體的實(shí)現(xiàn)類(lèi),例如 ReentrantLock 、CountDownLatch 、CyclicBarrier 這些的執(zhí)行過(guò)程來(lái)進(jìn)行分析。

到此這篇關(guān)于Java中的AQS同步隊(duì)列的文章就介紹到這了,更多相關(guān)java AQS同步隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven重復(fù)依賴問(wèn)題解決(同一個(gè)jar多個(gè)版本)
本文主要介紹了Maven重復(fù)依賴問(wèn)題解決(同一個(gè)jar多個(gè)版本),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
一文帶你熟練掌握J(rèn)ava中的日期時(shí)間相關(guān)類(lèi)
我們?cè)陂_(kāi)發(fā)時(shí),除了數(shù)字、數(shù)學(xué)這樣的常用API之外,還有日期時(shí)間類(lèi),更是會(huì)被經(jīng)常使用,比如我們項(xiàng)目中必備的日志功能,需要記錄異常等信息產(chǎn)生的時(shí)間,本文就帶各位來(lái)學(xué)習(xí)一下相關(guān)的日期時(shí)間類(lèi)有哪些2023-05-05
以Java代碼為例講解設(shè)計(jì)模式中的簡(jiǎn)單工廠模式
簡(jiǎn)單來(lái)說(shuō),工廠模式就是按照需求來(lái)返回一個(gè)類(lèi)型的對(duì)象,使用工廠模式的意義就是,如果對(duì)象的實(shí)例化與代碼依賴太大的話,不方便進(jìn)行擴(kuò)展和維護(hù),使用工廠的目的就是使對(duì)象的實(shí)例化與主程序代碼就行解耦.來(lái)具體看一下:2016-05-05
Springboot結(jié)合JDBC實(shí)現(xiàn)雙數(shù)據(jù)源實(shí)例
這篇文章主要為大家介紹了Springboot結(jié)合JDBC實(shí)現(xiàn)雙數(shù)據(jù)源實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Java實(shí)戰(zhàn)之吃貨聯(lián)盟訂餐系統(tǒng)
這篇文章主要介紹了Java實(shí)戰(zhàn)之吃貨聯(lián)盟訂餐系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
Mybatis使用foreach標(biāo)簽實(shí)現(xiàn)批量插入方式
這篇文章主要介紹了Mybatis使用foreach標(biāo)簽實(shí)現(xiàn)批量插入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法
這篇文章主要給大家介紹了關(guān)于struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10

