Java AQS 概述
AQS 概述
AQS(佇列同步器,AbstractQueuedSynchronizer),是用來構建鎖或其他同步元件的核心基礎框架(比如 ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch)
AQS的底層結構是:一個整型變數state表示同步狀態,一個內建的FIFO佇列(同步佇列)來實現資源獲取執行緒的同步等待排隊,若干個FIFO佇列(條件等待佇列)來完成持有資源執行緒的條件等待排隊。同步佇列如下:
AQS的使用方式
同步器的設計是基於模板方法模式的,其使用方式是繼承,子類通過繼承同步器並重寫指定的模板方法,模板方法分為3類:獨佔式獲取與釋放同步狀態、共享式獲取與釋放同步狀態、查詢佇列中的等待執行緒。在重寫過程中可以使用同步器提供的3個方法來管理同步狀態state:getState()、setState()和compareAndSetState(),因為它們能夠保證state的改變是併發安全的。
當實現一個自定義同步元件時,推薦在內部聚合/組合一個同步器的實現類,該自定義同步元件將通過呼叫同步器提供的模板方法來實現自己的同步語義。為什麼不直接使用同步器呢?因為同步器自身沒有實現任何同步介面,它僅僅是定義了若干管理同步狀態的方法,這樣有利於實現各種型別的同步元件
同步器與鎖的關係
同步器是實現鎖的關鍵,在鎖的實現中聚合/組合了一個同步器,利用該同步器實現了鎖的語義。
可以這樣理解二者之間的關係:鎖是面向使用者的,它定義了使用者與鎖的互動介面,隱藏了實現細節;而同步器面向的是鎖的實現者,它簡化了鎖的實現方式,包括遮蔽同步狀態管理、執行緒的排隊、等待與喚醒等底層操作。鎖和同步器很好地隔離了使用者和實現者所需關注的領域。
AQS:獲取與釋放同步狀態
1.獨佔式獲取同步狀態
檢視原始碼 AbstractQueuedSynchronizer.acquire(..)
public final void acquire(int arg) { //tryAcquire(..) 嘗試獲取同步狀態,由子類重寫,最經典的實現是互斥鎖:state=0(鎖自由),state>0(鎖被佔用) //acquireQueued(..) 進入同步佇列,阻塞等待獲取鎖 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
由於acquire(..)方法不響應中斷,因此AQS也提供了響應中斷、超時等待的方法:acquireInterruptibly(..)、tryAcquireNanos(..)
2.獨佔式釋放同步狀態
檢視原始碼 AbstractQueuedSynchronizer.release(..)
public final boolean release(int arg) { //tryAcquire(..) 嘗試釋放同步狀態,由子類重寫 if (tryRelease(arg)) { Node h = head; //unparkSuccessor(..) 喚醒被掛起的Node(執行緒) if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
3.共享式獲取同步狀態
檢視原始碼 AbstractQueuedSynchronizer.acquireShared(..)
public final void acquireShared(int arg) { //tryAcquireShared(..) 嘗試獲取同步狀態,由子類重寫,最經典的實現是訊號量:state>0(鎖很多),state<0(鎖不足) //doAcquireShared(..) 進入同步佇列,阻塞等待獲取鎖 if (tryAcquireShared(arg) < 0) doAcquireShared(arg); }
由於acquireShared(..)方法不響應中斷,因此AQS也提供了響應中斷、超時等待的方法:acquireSharedInterruptibly(..)、tryAcquireSharedNanos(..)
4.共享式釋放同步狀態
檢視原始碼 AbstractQueuedSynchronizer.
public final boolean releaseShared(int arg) { //tryReleaseShared(..) 嘗試釋放同步狀態,由子類重寫 if (tryReleaseShared(arg)) { //doReleaseShared(..) 喚醒被掛起的Node(執行緒) doReleaseShared(); return true; } return false; }