關(guān)于作者:CSDN內(nèi)容合伙人、技術(shù)專家, 從零開始做日活千萬級APP。
專注于分享各領(lǐng)域原創(chuàng)系列文章 ,擅長java后端、移動開發(fā)、人工智能等,希望大家多多支持。
一、導讀
我們繼續(xù)總結(jié)學習Java基礎(chǔ)知識,溫故知新。
1.1 CLH鎖
CLH(Craig, Landin, and Hagersten locks)是一種自旋鎖,能確保無饑餓性,提供先來先服務的公平性。
CLH鎖是一種基于鏈表的可擴展、高性能、公平的自旋鎖,申請線程只在本地變量上自旋,它不斷輪詢前驅(qū)的狀態(tài),如果發(fā)現(xiàn)前驅(qū)釋放了鎖就結(jié)束自旋。
二、概覽
AbstractQueuedSynchronizer 是抽象隊列同步器,是一種用來構(gòu)建鎖和同步器的框架。
AQS主要做了三件事情
- 同步狀態(tài)的管理
- 線程的阻塞和喚醒
- 同步隊列的維護
AQS 定義了同步器的基本操作,如獲取、釋放和狀態(tài)管理,并提供了一個等待隊列來管理等待資源的線程,解決了在實現(xiàn)同步器時涉及的大量細節(jié)問題,例如自定義標準同步狀態(tài)、FIFO 同步隊列。
基于 AQS 來構(gòu)建同步器可以帶來很多好處。它不僅能夠極大地減少實現(xiàn)工作,而且也不必處理在多個位置上發(fā)生的競爭問題。
三、使用場景
AQS 是一個相對底層的同步器框架,對于一些常見的同步需求,Java 并發(fā)庫已經(jīng)提供了許多高級封裝,如 ReentrantLock、ReadWriteLock、Semaphore 等,這些高級封裝已經(jīng)為我們提供了更簡單易用的接口和功能。因此,在應用開發(fā)中,直接使用 AQS 的場景相對較少,更多的是通過使用它的子類來實現(xiàn)具體的同步機制。
常用的同步器有:
-
獨占鎖(如 ReentrantLock):AQS 提供了 acquire(int arg) 和 release(int arg) 等方法,開發(fā)人員可以繼承 AQS 并實現(xiàn)自定義的同步器來實現(xiàn)獨占鎖。通過控制同步狀態(tài)(通過 getState() 和 setState(int newState) 方法),以及管理等待線程(通過等待隊列),AQS 可以提供可重入鎖、公平鎖等不同類型的獨占鎖。
-
共享鎖(如 ReadWriteLock):AQS 也可以用于實現(xiàn)共享鎖機制,例如 ReentrantReadWriteLock。通過 acquireShared(int arg) 和 releaseShared(int arg) 等方法,開發(fā)人員可以自定義實現(xiàn)共享鎖的邏輯。AQS 提供了對多個讀線程和寫線程的管理和協(xié)調(diào),以及對讀線程的優(yōu)化。
-
實現(xiàn)其他同步工具:AQS 的框架還可以用于實現(xiàn)其他類似的同步工具,如信號量(Semaphore)、倒計時器(CountDownLatch)、循環(huán)屏障(CyclicBarrier)等。
通過繼承 AQS 并自定義同步器的行為,可以實現(xiàn)不同的同步機制。
3.1 AQS 對資源的共享方式
-
Exclusive(獨占):只有一個線程能執(zhí)行,如ReentrantLock。
資源鎖可分為公平鎖和非公平鎖:
-
公平鎖:按照線程在隊列中的排隊順序(FIFO),先到者先拿到鎖。
-
非公平鎖:當線程要獲取鎖時,無視隊列順序直接去搶鎖,誰搶到就是誰的(被喚醒的線程和新來的線程重新競爭鎖)。
- Share(共享):多個線程可同時執(zhí)行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我們都會在后面講到。
四、原理
AQS大致流程如下:
1、當某一線程獲取鎖后,將state值+1,并記錄下當前持有鎖的線程。
2、再有線程來獲取鎖時,判斷這個線程與持有鎖的線程是否是同一個線程,如果是,將state值再+1,如果不是,阻塞線程(調(diào)用 LockSupport.park(this)掛起線程)。
3、當線程釋放鎖時,將state值-1。
4、當state值減為0時,表示當前線程徹底釋放了鎖。
5、然后將記錄當前持有鎖的線程的那個字段設置為null,并喚醒其他線程,使其重新競爭鎖
4.1 原理
AQS使用一個 Volatile的 int類型的成員 state 變量來表示同步狀態(tài),通過內(nèi)置的FIFO隊列來完成資源獲取的排隊工作(雙向鏈表,多線程爭用資源被阻塞時會進入此隊列),然后通過CAS完成對State值的修改。
其并發(fā)控制的核心是鎖的獲取與釋放,鎖的實現(xiàn)方式有很多種,AQS采用的是一種改進的CLH鎖。
當state=0表示釋放了鎖,當state>0表示獲得鎖
/**
* The synchronization state.
*/
private volatile int state;
封裝一個Node,包含前節(jié)點,后節(jié)點,組成一個雙向隊列。
private transient volatile Node head;
private transient volatile Node tail;
CLH(Craig,Landin,and Hagersten)隊列是一個虛擬的雙向隊列(虛擬的雙向隊列即不存在隊列實例,僅存在結(jié)點之間的關(guān)聯(lián)關(guān)系)。
AQS是將每條請求共享資源的線程封裝成一個CLH鎖隊列(FIFO同步隊列)的一個結(jié)點(Node)來實現(xiàn)鎖的分配。
如果線程獲取當前同步狀態(tài)失敗,AQS會將當前線程的信息封裝成一個Node節(jié)點,加入同步隊列中,并且阻塞該線程,當同步狀態(tài)釋放,則會將隊列中的線程喚醒,重新嘗試獲取同步狀態(tài)。
我們看下node源碼:
static final class Node {
/** 共享節(jié)點 */
static final Node SHARED = new Node();
/** 獨占節(jié)點 */
static final Node EXCLUSIVE = null;
當前節(jié)點在隊列中的狀態(tài)
volatile int waitStatus;
前驅(qū)指針
volatile Node prev;
后繼指針
volatile Node next;
表示處于該節(jié)點的線程
volatile Thread thread;
指向下一個處于CONDITION狀態(tài)的節(jié)點
Node nextWaiter;
}
五、 推薦閱讀
【Java基礎(chǔ)】原子性、可見性、有序性
【Java基礎(chǔ)】java可見性之 Happens-before
【Java基礎(chǔ)】java-android面試Synchronized
【Java基礎(chǔ)】java-android面試-線程狀態(tài)
【Java基礎(chǔ)】線程相關(guān)
【Java基礎(chǔ)】java 異常
【Java基礎(chǔ)】java 反射
【Java基礎(chǔ)】java 泛型
【Java基礎(chǔ)】java注解
【Java基礎(chǔ)】java動態(tài)代理
【Java基礎(chǔ)】Java SPI
【Java基礎(chǔ)】Java SPI 二 之 Java APT
【Java基礎(chǔ)】 jvm 堆、棧、方法區(qū) & java 內(nèi)存模型
【Java基礎(chǔ)】volatile關(guān)鍵字
【Java基礎(chǔ)】線程同步類 CountDownLatch文章來源:http://www.zghlxwxcb.cn/news/detail-549044.html
【Java基礎(chǔ)】CAS (Compare And Swap) 操作文章來源地址http://www.zghlxwxcb.cn/news/detail-549044.html
到了這里,關(guān)于【Java基礎(chǔ)】AQS (AbstractQueuedSynchronizer) 抽象隊列同步器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!