一、AQS 是什么
- Abstract Queued Synchronizer ,翻譯過來就是“抽象的排好隊的同步器”。 AQS 是一個用來構建鎖和同步器的框架。
- 是用來構建鎖或者其他同步器組件的重量級基礎框架及整個JUC體系的基石,通過內置的FIFO隊列來完成線程獲取資源的排隊工作,并通過一個int類型變量表示持有鎖的狀態。
二、AQS 有什么用
- 使用 AQS 能簡單高效的構造出大量被廣泛應用的同步器,比如 ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等,都是基于 AQS 的。
- 我們也可以使用 AQS 輕松構造出符合特定需求的同步器。
- 加鎖會導致阻塞,有阻塞就需要排隊,實現排隊必然需要有某種形式的隊列(CLH)來進行管理
三、AQS 的核心思想
- 如果被請求的共享資源空閑,則將當前請求資源的線程設置為有效的工作線程,
- 并且將共享資源設置為鎖定狀態。
- 如果被請求的共享資源被占用,那么就需要一套線程阻塞等待以及被喚醒時鎖分配的機制
- 這個機制是 AQS 使用 CLH隊列鎖實現的,即將暫時獲取不到鎖的線程加入到隊列中。
- CLH 是一個虛擬的雙向FIFO隊列,AQS 將每個請求共享資源的線程封裝成Node 節點,并放入CLH 中
- AQS 使用一個volatile 的 int 類型的變量來表示同步狀態,通過內置的FIFO隊列來完成資源獲取的排隊工作將每條要去搶占資源的線程封裝成一個Node節點來實現鎖的分配,通過CAS完成對State值的修改
四、AQS 基本結構
五、AQS 怎么用
- 創建任一基于AQS實現的同步器
- 創建多個線程,并分別使用同步器的 lock 和 unlock 方法
- 會發現,這些線程是一個一個執行的,而非一起執行的,說明鎖成功了。
六、進一步理解鎖和同步器的關系
- 鎖,面相鎖的使用者,定義了程序員和鎖交互的使用層API
- 同步器,面相鎖的實現者,
七、源碼分析
- Lock --> Syn -----> AQS,所以Lock 最終繼承的是AQS
- lock(),acquire(),tryAcquire(arg),addWaiter(Node.EXCLUSIVE),addQueued(addWaiter(Node.EXCLUSIVE),arg)
- head和tail
- 是什么?
head 和 tail 分別是頭指針和尾指針,分別指向隊列中的頭節點和尾節點 - 有什么用?
方便出隊和入隊操作,也方便邏輯判斷(暫時是這么理解的)
- 是什么?
- 哨兵節點有什么用?
隊列中的哨兵節點在并發編程中起到了重要的作用。哨兵節點是一種特殊的節點,通常用于幫助管理和控制并發隊列的行為。在使用隊列實現同步器時,哨兵節點可以用來簡化邏輯、提高效率和確保線程安全。 - 確實有點復雜,但我感覺不應該去理解源碼的每一行代碼,而應該從整體設計思想去理解就好了,以后有機會自己實現一個簡陋的example。
- 出隊操作:第一個真實節點B搶占資源成功,哨兵節點出隊,B變成新的哨兵節點
參考博客 一文讓你徹底搞懂AQS(通俗易懂的AQS)
聲明:僅用于學習,不做其他用途。本文對所引用的博客,進行了摘抄、整理,其中AQS 的知識點并不詳盡,希望未來有機會能夠更加深入地講解它。
擴展
一、可重入鎖
- 可重入鎖,又名遞歸鎖。是指在同一個線程,在外層方法獲取鎖的時候,再進入線程的內層方法時,會自動獲取鎖(前提,鎖對象得是同一個對象),不會因為之前已經獲取過還沒釋放而阻塞
- Java 中 ReentrantLock 和 Synchronized 都是可重入鎖
- 可重入鎖的一個優點是可以一定程度避免死鎖
- 一句話概括:同一個線程可以多次獲得屬于自己的同一把鎖,一定程度避免死鎖
二、 LockSupport
- 是什么
LockSupport 用于創建鎖和其他同步類的基本線程阻塞原語。 - 有什么用
- 對于原有鎖支持線程等待喚醒機制(wait/nofify)的加強、改良版
- LockSupport 的 park()和 unpark()的作用分別是阻塞線程和解除阻塞線程
- 兩個三角形:
- (synchronized,wait,notify)
- (lock,await,signal)
- 核心思想
- LockSupport 使用了一種名為 Permit (許可)的概念來做到阻塞和喚醒線程的功能,每個線程都有一個許可(permit),permit 只有兩個值1和0,默認為零
- 可以把許可看成一種(0,1)信號量(Semaphore),但是與Semaphore 不同的是,許可的累加上限是1
- 怎么用
- LockSupport.park() 阻塞
- LockSupport.unpark(a) 喚醒啊
- 什么叫阻塞,什么叫喚醒?
- 阻塞就是說,在那一步線程就轉換為“等待執行”的狀態
- 喚醒就是說,讓線程繼續執行
- unpark()可以在park()之前執行
- 優點
LockSupport 是一個線程阻塞工具類,所有的方法都是靜態方法,可以讓線程在任意位置阻塞,阻塞之后也有對應的喚醒方法。歸根結底,LockSupport 調用 Unsafe 中的native 代碼。 - 為什么可以先喚醒線程后阻塞線程?
因為unpark 獲得了一個憑證,之后,在調用 park 方法,就可以名正言順的憑證消費,故不會阻塞。