AQS全稱是AbstractQueuedSynchronizer,是JDK提供的一個同步器設計框架,很多併發數據結構如ReentrantLock、ReentrantReadWriteLock、Semaphore等都是基於AQS來實現的,下面來分析一下AQS的原理。
一、底層數據結構
AQS底層維護了一個state(代表共享資源)和一個CLH隊列(代表線程等待隊列)
state:state是一個volatile int型變量,用以表示共享資源的使用情況。
head、tail:分別表示CLH隊列的頭節點和尾節點。關於CLH隊列原理可以看這篇博客:
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
AQS定義了兩種資源:
一種是Exclusive(獨佔方式,同一時間只有一個線程能夠訪問,比如ReentrantLock);
一種是Share(共享方式,同一時間允許多個線程訪問,比如Semaphore)。
二、AQS原理
AQS被定義爲一個同步器框架,那麼爲什麼說AQS是一個“框架”呢,框架的定義應該是使用者根據框架的要求去實現一些業務邏輯,然後底層由框架自己去完成,比如使用Hadoop大數據框架的時候,我們只需要定義好Map/Reduce函數就可以了,其他複雜的底層操作就有Hadoop幫我們完成。AQS也應該是這樣一個東西,事實上,AQS也確實是這樣做的,它只需要開發者實現其中的幾個函數,其他的底層操作比如CLH隊列操作、CAS自旋等等都是由AQS框架來實現。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
上面的幾個方法就是AQS要我們實現的函數,這幾個方法都是AQS提供給開發者編寫代碼邏輯的,因此每個方法裏面都定義爲直接拋出UnsupportedOperationException,下面解釋一下這幾個函數的作用。
tryAcquire:嘗試獲取獨佔資源。成功返回true,失敗返回false。
tryRelease:嘗試釋放獨佔資源。成功返回true,失敗返回false。
tryAcquireShared:嘗試獲取共享資源。成功返回一個非負數,代表剩餘資源(0就表示沒有可用資源),負數表示失敗。
tryReleaseShared:嘗試釋放共享資源。成功返回true,失敗返回false。
isHeldExclusively:判斷線程是否正在獨佔資源。
對於獨佔方式的同步器,只需要實現tryAcquire-tryRelease;而對於共享方式的同步器則只需要實現tryAcquireShared-tryReleaseShared就行了,當然如果自定義同步器需要同時實現獨佔和共享方式的話也可以,比如讀寫鎖ReentrantReadWriteLock,其中讀鎖是共享的,寫鎖是獨佔的。
三、源碼解析
AQS中對於資源獲取的頂層接口是acquire-release和acquireShare-releaseShare,下面從這些頂層接口來分析:
1、acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
代碼邏輯:
(1)嘗試獲取資源(tryAcquire),成功則返回,失敗則跳轉到(2)
(2)請求將該線程加入線程等待隊列的尾部,並標記爲獨佔模式(addWaiter(Node.EXCLUSIVE)),並不斷請求隊列智鬥獲取到資源,如果該過程被打斷,則跳轉到(3),失敗則返回。
(3)自我中斷(selfInterrupt)
(4)完成
2、release
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
代碼邏輯:
(1)嘗試釋放資源,如果成功則跳轉到(2),失敗則返回false
(2)獲取線程等待隊列的頭節點,如果頭節點線程不爲空且處於等待狀態,則喚醒該線程(unparkSuccessor)
3、acquireShared
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
代碼邏輯:
(1)嘗試獲取共享資源,如果獲取失敗則調用doAcquireShared自旋來請求共享資源。
4、releaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
代碼邏輯:
(1)嘗試釋放共享資源,釋放成功則喚醒等待隊列中的後繼節點來獲取資源。
5、如果需要響應中斷程序的話,AQS還提供了acquireInterruptibly和acquireSharedInterruptibly函數來響應。
4、實際例子:ReentrantLock解析
一般用AQS來實現同步器主要使用內部類的方式來實現的,ReentrantLock裏面就是一個比較經典的實現,看一下這個內部類的實現源碼:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
@ReservedStackAccess
final boolean nonfairTryAcquire(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) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
這裏定義的Sync同步器類仍然是一個抽象類,是因爲ReentrantLock同時支持公平鎖和非公平鎖,而這兩種同步方式是不一樣的,所以先定義一個Sync類作爲兩種實現的基類,後面會看到具體的公平鎖和非公平鎖的實現。
由於ReentrantLock本身是一個獨享鎖,因此Sync類主要重寫了AQS裏面的tryRelease和isHeldExclusively方法,同時定義了非公平鎖的nonfairTryAcquire方法作爲請求資源的方法。
非公平鎖實現:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
其實基類Sync完完全全就是一個非公平鎖的實現,所以NonfairSync並沒有新代碼,tryAcquire直接調用nonfairTryAcquire。
公平鎖實現:
static final class FairSync extends Sync {
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
公平鎖與非公平鎖的區別也就是資源請求的方式不一樣,所以在基類Sync的基礎上重寫tryAcquire方法,順序喚醒等待隊列中的線程。
基於AQS實現了公平鎖和非公平鎖之後,如何實現上鎖和解鎖的功能也就十分簡單了,源碼直接用一行代碼就實現了:
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}