JUC併發程式設計基石AQS原始碼之結構篇
前言
AQS(AbstractQueuedSynchronizer)算是JUC包中最重要的一個類了,如果你想了解JUC提供的併發程式設計工具類的程式碼邏輯,這個類絕對是你繞不過的。我相信如果你是第一次看AQS原始碼肯定是一臉懵逼,一個個方法跳來跳去一會就繞蒙了。所以把整個程式碼骨架搞明白是你看懂AQS原始碼的第一步。本篇文章只說程式碼結構,之後的篇章會講解AQS具體的執行邏輯。
頂級介面Lock
public interface Lock { void lock(); void unlock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; Condition newCondition(); }
我們一直強調面向介面程式設計的思想,看原始碼也是先從介面入手。Lock算是JUC提供的鎖中的頂級介面,我們平常使用的ReentrantLock、ReadWriteLock等直接或間接的都實現了這個介面。這個介面主要是用來規定我們怎樣去加鎖和解鎖,將加鎖和解鎖的方法暴露出去。下面的虛擬碼是一個典型的加鎖解鎖方式,多麼簡單,這就是面向介面的藝術。
Lock l = ...;
l.lock();
try {
// 自己的邏輯程式碼
} finally {
l.unlock();
}
程式碼結構
到現在我們知道了怎麼加鎖和解鎖的方式,下一步自己通過介面去實現一個加鎖類。這個比你直接看原始碼更重要。Doug Lea(AQS原始碼作者)大神在AQS類註解上就給我們提供了一個示例類Mutex。看原始碼類註解和方法註解也是你理解原始碼的一個重要渠道,還可以鍛鍊自己的英文。
實現Lock介面
首先要實現Lock介面,將加鎖和解鎖方法暴露出去。我們以加鎖為例
class Mutex implements Lock, java.io.Serializable {
public void lock() {
sync.acquire(1);
}
程式碼很簡單,呼叫了sync.acquire(1)方法,所以這個方法是加鎖邏輯的入口,往下看
內部類繼承AQS
現在將加鎖方法暴露出去了,具體的加鎖邏輯則需要AQS類了。AQS是一個抽象類,我們要使用它則需要繼承,實現抽象方法。AQS加鎖採用的是模板的設計模式,加鎖以及鎖失敗的後續處理的整體流程程式碼已經實現,我們只需要實現我們需要的具體加鎖方式即可。
class Mutex implements Lock, java.io.Serializable {
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
// 內部類繼承AQS
private static class Sync extends AbstractQueuedSynchronizer {
// 實現抽象方法
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}
}
我們會發現Sync繼承了AQS,但是沒有sync.acquire()這個方法,那麼這個方法肯定來源於父類了。
AQS的acquire方法
public abstract class AbstractQueuedSynchronizer{
//加鎖的入口方法,模板的設計模式
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//具體的加鎖邏輯,需要自己實現
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
acquire方法定義了整個的加鎖流程,而且使用了設計模式中的模板模式。
整體呼叫流程
從上面的流程可以看出,就這四個步驟就涉及到了三個類
-
Mutex.lock為暴露出去的加鎖方法
-
AQS.acquire是加鎖的模板方法,實現了加鎖邏輯的整個流程
-
Sync.tryAcquire,圖中的綠色部分,是一個抽象方法需要自己實現,針對不同的鎖型別如公平鎖、非公平鎖、共享鎖、獨佔鎖等有不同的實現方式。
-
AQS.acquireQueued是加鎖失敗後的邏輯,將執行緒入隊,這個後面講AQS原始碼會重點講。
解鎖操作的流程和加鎖類似,讀者可以自己看一下解鎖的流程。
自定義加鎖類的原始碼
下面的程式碼是Mutex類的整體程式碼,有需要的可以在自己的IDE中感受一下整體的結構。
class Mutex implements Lock, java.io.Serializable {
public static void main(String[] args) {
Lock lock = new Mutex();
lock.lock();
try {
}finally {
lock.unlock();
}
}
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Reports whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}
// Acquires the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// Releases the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// Provides a Condition
Condition newCondition() {
return new ConditionObject();
}
// Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
public boolean tryLock() {
return sync.tryAcquire(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
下一篇我們將根據上面所說的來分析ReentrantLock類的程式碼結構
如有不實,還望指