1. 程式人生 > >【Java】J.U.C併發包 - AQS機制

【Java】J.U.C併發包 - AQS機制

簡介

Java併發包(java.util.concurrent)中提供了很多併發工具,這其中,很多我們耳熟能詳的併發工具,譬如ReentrantLock、Semaphore,CountDownLatch,CyclicBarrier,它們的實現都用到了一個共同的基類 - AbstractQueuedSynchronizer,簡稱AQS。AQS提供了一種原子式管理同步狀態、阻塞和喚醒執行緒功能以及佇列模型的簡單框架。是一個用來構建鎖和同步器的框架,使用AQS能簡單且高效地構造出應用廣泛的大量的同步器,比如我們提到的ReentrantLock,Semaphore,其他的諸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基於AQS的。

設計思想

同步器背後的基本思想非常簡單,可以參考AQS作者 Doug Lea 的論文:The java.util.concurrent Synchronizer Framework。同步器一般包含兩種方法,一種是acquire,另一種是release。

  • acquire操作阻塞呼叫的執行緒,直到同步狀態允許其繼續執行。
  • release操作則是改變同步狀態,使得一或多個被acquire阻塞的執行緒繼續執行。

其中acquire操作虛擬碼如下:

while (synchronization state does not allow acquire) {
    enqueue current thread if
not already queued; possibly block current thread; } dequeue current thread if it was queued;

release操作虛擬碼如下:

update synchronization state;
if (state may permit a blocked thread to acquire)
    unblock one or more queued threads;

為了實現上述操作,需要下面三個基本元件的相互協作:

  • 同步狀態的原子性管理;
  • 執行緒的阻塞與解除阻塞;
  • 佇列的管理;

AQS框架藉助於兩個類:Unsafe(提供CAS操作) 和 LockSupport(提供park/unpark操作)。

1. 同步狀態的原子性管理

AQS類使用單個int(32位)來儲存同步狀態,並暴露出getStatesetState以及compareAndSet操作來讀取和更新這個狀態。該整數可以表現任何狀態。比如, Semaphore 用它來表現剩餘的許可數,ReentrantLock 用它來表現擁有它的執行緒已經請求了多少次鎖;FutureTask 用它來表現任務的狀態(尚未開始、執行、完成和取消)。

如JDK的文件中所說,使用AQS來實現一個同步器需要覆蓋實現如下幾個方法,並且使用getStatesetStatecompareAndSetState這幾個方法來設定或者獲取狀態。

  • boolean tryAcquire(int arg)

  • boolean tryRelease(int arg)

  • int tryAcquireShared(int arg)

  • boolean tryReleaseShared(int arg)

  • boolean isHeldExclusively()

以上方法不需要全部實現,根據獲取的鎖的種類可以選擇實現不同的方法,支援獨佔(排他)獲取鎖的同步器應該實現tryAcquiretryReleaseisHeldExclusively而支援共享獲取的同步器應該實現tryAcquireSharedtryReleaseSharedisHeldExclusively。下面以 CountDownLatch 舉例說明基於AQS實現同步器, CountDownLatch 用同步狀態持有當前計數,countDown方法呼叫 release從而導致計數器遞減;當計數器為0時,解除所有執行緒的等待;await呼叫acquire,如果計數器為0,acquire 會立即返回,否則阻塞。

參考資料