Java中的AQS同步隊列問題詳解
AQS 同步隊列
1、AQS 介紹
AQS
是 AbstractQueuedSynchronizer 的縮寫,他是一個抽象同步類,為 JUC
包下的大多數同步工具提供瞭核心實現,例如 ReentrantLock 的底層就是使用同步隊列。AQS 提供一套基礎的機制來實現線程的同步、阻塞與喚醒、等待隊列等功能,也就是想要深入學習線程工具類,這個同步隊列就必須得掌握。
1.1、類圖關系
下面是整個 AQS
的類結構實現(從源碼中直接打印的圖,平常學習源碼也可以這樣打印出來觀察整個程序的運行情況,有助於理解),從圖中我們不難發現 AQS
內部持有兩個 Node 類型的 head 、tail 屬性,我們在什麼時候會接觸到頭尾節點的定義,大傢都是有經驗的開發人員,肯定都能想到是鏈表當中。
在屬性列中我們可以看見一個 state:int
這樣的一個狀態字段,Lock 的重入特性就是根據此來實現的,可以表示當前線程的鎖重入次數。整個類繼承自 AbstractOwnableSynchronizer 類,自然擁有對於其父類中屬性的一些控制權,而裡面的 Thraed
的線程就是表示當前持有鎖的線程,在整個鎖過程中具有很重要的地位。
1.2、節點剖析
當然鏈表隻是一種組織存儲形式的一種數據結構,這裡叫做 FIFO
雙向隊列,至於為什麼是雙向的呢,看一下 Node
的節點定義就能明白,一個節點中含有 prev 、next 節點來快速訪問前驅和後繼節點,不就是典型的雙向形式呢。
相信大傢在看到這個類字段的屬性名定義之後就能才出來其的作用,但是這裡還是介紹一下主要的幾個字段含義,印證大傢的猜想。
屬性 | 作用 |
---|---|
thread | 表示當前節點封裝的具體線程 |
SHARED | 表示當前線程是獲取共享資源時被阻塞 |
EXCLUSIVE | 表示當前線程是獲取獨占資源時被掛起 |
prev | 當前節點的前驅節點 |
next | 當前節點的後繼節點 |
waitStatus | 記錄當前線程的等待狀態,其狀態取值就是下面的四個字段 |
CANCELLED | 取消線程 |
SIGNAL | 線程需要被喚醒 |
CONDITION | 線程在 condition 中等待 |
PROPAGATE | 釋放共享資源時需要通知其餘節點線程 |
2、AQS 實現原理
上面我們知道瞭 AQS
其實就是一個雙向的隊列,如下圖的結構一樣。在線程獲取鎖失敗的情況下,會被封裝成一個 Node 節點而插入到隊列當中;當其他的線程釋放鎖之後又會從隊列中喚醒一個節點去爭搶鎖。
2.1、隊列初始化
通過源碼我們可以發現,在 AQS
進行初始化的時候的並沒有對 head 、tail 進行初始化,而這兩個節點是控制整個隊列的,也就是說一開始整個隊列處於 null 狀態。
protected AbstractQueuedSynchronizer() {}
當第一個線程爭搶鎖失敗之後會封裝成 Node 進入到同步隊列當中,這個時候就會進行判斷,如果當前隊列為空就會進行初始化(未進行初始化),初始化完成之後就將當前線程節點接在隊列的尾部。
private final void initializeSyncQueue() { Node h; if (HEAD.compareAndSet(this, (Void)null, h = new Node())) { this.tail = h; } }
2.2、追加節點
追加節點的操作就是簡單的鏈表尾部添加節點的過程瞭,這裡就不做過多的贅述。這裡來看看前面初始化時會添加一個額外的節點在隊列中,其實這個節點就是代表當前已經獲取瞭鎖的線程,至於為什麼這麼設置,大傢往後看就明白瞭。
3、AQS 喚醒動作
在進行線程喚醒的過程中,會優先喚醒當前持有鎖線程的下一個節點線程。
- head 指針指向下一個節點;
- 原來頭結點的 next 指向 null;
- 當前頭結點的 prev 指向 null;
- 當前頭結點的 thread 指向 null。
這樣就完成線程的喚醒操作瞭,但是這樣來講其實是不完美的,因為 AQS
隻是一個抽象的統一工具,本身並沒有對業務進行規范,還是要結合具體的實現類,例如 ReentrantLock 、CountDownLatch 、CyclicBarrier 這些的執行過程來進行分析。
到此這篇關於Java中的AQS同步隊列的文章就介紹到這瞭,更多相關java AQS同步隊列內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談Java並發之同步器設計
- ReentrantLock從源碼解析Java多線程同步學習
- Java面試必備之AQS阻塞隊列和條件隊列
- Java多線程同步工具類CyclicBarrier的使用
- Java多線程之深入理解ReentrantLock