同步类的基础AbstractQueuedSynchronizer(AQS)
同步類的基礎AbstractQueuedSynchronizer(AQS)
我們之前介紹了很多同步類,比如ReentrantLock,Semaphore, CountDownLatch, ReentrantReadWriteLock,FutureTask等。
AQS封裝了實現同步器時設計的大量細節問題。他提供了FIFO的wait queues并且提供了一個int型的state表示當前的狀態。
根據JDK的說明,并不推薦我們直接使用AQS,我們通常需要構建一個內部類來繼承AQS并按照需要重寫下面幾個方法:
- tryAcquire
- tryRelease
- tryAcquireShared
- tryReleaseShared
- isHeldExclusively
在這些方法中,我們需要調用getState, setState 或者 compareAndSetState這三種方法來改變state值。
上面的方法提到了兩種操作,獨占操作(如:ReentrantLock)和共享操作(如:Semaphore,CountdownLatch)。
兩種的區別在于同一時刻能否有多個線程同時獲取到同步狀態。
比如我們運行同時多個線程去讀,但是通知只允許一個線程去寫,那么這里的讀鎖就是共享操作,而寫鎖就是獨占操作。
在基于QAS構建的同步類中,最基本的操作就是獲取操作和釋放操作。而這個state就表示的是這些獲取和釋放操作所依賴的值。
State是一個int值,你可以使用它來表示任何狀態,比如ReentrantLock用它來表示所有者線程重復獲取該鎖的次數。Semaphore用它來表示剩余的許可量,而FutureTask用它來表示任務的狀態(開始,運行,完成或者取消)。當然你還可以自定義額外的狀態變量來表示其他的信息。
下的偽代碼表示的是AQS中獲取和釋放操作的形式:
Acquire:while (!tryAcquire(arg)) {enqueue thread if it is not already queued;possibly block current thread;}Release:if (tryRelease(arg))unblock the first queued thread;獲取操作,首先判斷當前狀態是否允許獲取操作,如果如果不允許,則將當前的線程入Queue,并且有可能阻塞當前線程。
釋放操作,則先判斷是否運行釋放操作,如果允許,則解除queue中的thread,并運行。
我們看一個具體的實現:
public class AQSUsage {private final Sync sync= new Sync();private class Sync extends AbstractQueuedSynchronizer{protected int tryAcquireShared(int ignored){return (getState() ==1 )? 1: -1;}protected boolean tryReleaseShared(int ignored){setState(1);return true;}}public void release() {sync.releaseShared(0);}public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(0);} }上面的例子中,我們定義了一個內部類Sync,在這個類中我們實現了tryAcquireShared和tryReleaseShared兩個方法,在這兩個方法中我們判斷并設置了state的值。
sync.releaseShared和sync.acquireSharedInterruptibly會分別調用tryAcquireShared和tryReleaseShared方法。
前面我們也提到了很多同步類都是使用AQS來實現的,我們可以再看看其他標準同步類中tryAcquire的實現。
首先看下ReentrantLock:
final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}ReentrantLock只支持獨占鎖。所以它需要實現tryAcquire方法。除此之外它還維護了一個owner變量來保存當前所有者線程的標志符,從而來實現可重入鎖。
我們再看下Semaphore和CountDownLatch的實現,因為他們是共享操作,所以需要實現tryAcqureShared方法:
final int tryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}本文的例子請參考https://github.com/ddean2009/learn-java-concurrency/tree/master/AQS
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
更多內容請訪問 flydean的博客
總結
以上是生活随笔為你收集整理的同步类的基础AbstractQueuedSynchronizer(AQS)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 由于不当的执行顺序导致的死锁
- 下一篇: 非阻塞同步机制和CAS