AQS原始碼分析
AbstractQueuedSynchronized(AQS)是一個抽象的佇列同步器,它本身是一個抽象類,提供一個FIFO的等待佇列來存放阻塞資源和一個state狀態來儲存鎖的資訊。在實際的使用中,比如ReentrantLock和ReentrantReadWirteLock中,它們本身都有一個Syn類來繼承AQS並做一些狀態操作。
鎖的使用過程大概如下,在實際使用鎖的過程中,比如我們使用一個lock()函式,同步類可以幫助我們通過tryAcquire來獲取鎖,如果可以獲取則state+1,如果不可以則新建節點放入同步佇列中,然後把這個執行緒阻塞。當我們使用一個realease()函式時,如果執行緒是持有鎖的執行緒則狀態減一,如果狀態為0則喚醒後繼節點。這裡強調一下lock的作用,其實就是用來分配狀態和阻塞節點,而unlock就是釋放狀態和喚醒節點。
AQS中程式碼結構如下,Node代表的是我們所說的佇列中的節點。ConditionObject中放的是有關Condition相關的實現方法。和Lock(unLock)相關的函式包括兩種,獨佔鎖函式和共享鎖函式,典型的就是ReentrantLock和ReentrantReadWirteLock。
Node
/**標記一個節點是共享節點 */
static final Node SHARED = new Node();
/** 表示一個節點是獨佔節點 */
static final Node EXCLUSIVE = null;
/** 等待狀態是要刪除 */
static final int CANCELLED = 1;
/** 喚醒後繼節點 */
static final int SIGNAL = -1;
/**等待條件 */
static final int CONDITION = -2;
/**
* 下一個獲得的共享無條件傳播
*/
static final int PROPAGATE = -3;
/**
* 等待狀態,前面四個,還有一個值是0,表示不是上面的四個。
*/
volatile int waitStatus;
/**
*前驅節點
*/
volatile Node prev;
/**
*後集結點
*/
volatile Node next;
/**
* 節點中放的是執行緒
*/
volatile Thread thread;
Condition
Condition在AQS中具體實現了,在其他的同步類中我們可以直接使用它,通過設定一個條件,在合適的時候通過呼叫await使一個執行緒沉睡並釋放鎖,當其他執行緒呼叫singal方法時會喚醒那個執行緒。我們通常可以將condition視為一個多執行緒之間通訊的工具。
Condition本身也維護一個等待條件佇列,整個await過程如下:
首先將該執行緒對應的節點放進條件等待佇列中去,然後釋放執行緒持有的全部鎖,判斷節點是否還在AQS的佇列中(這裡沒有被singal前是沒有被在佇列中的),因此這裡這個執行緒是會被掛起的。當後面signal方法被執行時,這裡在條件佇列中的執行緒還是會重新放在AQS的佇列中去。
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();//將節點放到condition佇列中去
int savedState = fullyRelease(node);//釋放所有資源返回資源數目
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//不在AQS佇列中,阻塞
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//獲取自己之前所有的資源
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal方法用於喚醒在條件佇列中頭結點的執行緒,如果條件佇列中的頭結點不為空,那麼就執行doSingal()函式。維護條件佇列中的節點,將頭結點放入AQS尾部,等待自己釋放鎖後,原來的await執行緒就可以獲取資源執行了。
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&//將頭結點放到AQS
(first = firstWaiter) != null);
}
/**
* Transfers a node from a condition queue onto sync queue.
* Returns true if successful.
* @param node the node
* @return true if successfully transferred (else the node was
* cancelled before signal)
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);//把節點放進AQS的尾部
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))//喚醒節點,進行資源搶佔
LockSupport.unpark(node.thread);//這裡其實狀態並不正常
return true; }
acquire()
獨佔鎖模式下可以通過這個函式來獲取鎖,獲取獨佔鎖的過程是這樣的。首先使用tryAcquire函式,這個函式在AQS中只是一個介面,具體實現在其實現類中實現,可以參照前面reentrantLock中的實現,主要作用是用來獲取資源,如果獲取到資源直接返回。否則為該執行緒新設定一個節點然後放到佇列後面,然後在佇列中找到頭結點後面的節點,然後判斷此過程中是否需要阻塞該執行緒。過程如下:
具體過程可以看reentrantLock分析。
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
release()
獨佔模式下釋放資源,執行tryRealease(1)嘗試釋放鎖,如果執行緒不一致則丟擲異常,否則狀態為0 則返回true,喚醒後繼節點。
public final boolean release(int arg) {
if (tryRelease(arg)) {//嘗試釋放鎖,其實就是狀態減一。
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//成功則喚醒後繼節點
return true;
}
return false;
}
acquiredShare()
共享鎖的情況下同樣是要維護一個佇列和一個狀態,但是在這裡把state分成兩個部分,高16為用於表示讀鎖的狀態,低16為用來表示寫鎖的狀態。
共享模式下獲得鎖的過程比較複雜,它的過程簡單描述如下:
/**
* 忽略中斷,以共享的模式獲取鎖 Implemented by
* first invoking at least once {@link #tryAcquireShared},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquireShared} until success.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquireShared} but is otherwise uninterpreted
* and can represent anything you like.
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)//嘗試獲取鎖,如果沒有獲得,就做一些嘗試操作
doAcquireShared(arg);//失敗則進入等待佇列一直到獲取資源為止
}
releaseShared()
共享模式下釋放資源分為兩步,首先嚐試釋放共享資源,如果成功則進行後續的喚醒操作。否則的話直接失敗。
/**
* Releases in shared mode. Implemented by unblocking one or more
* threads if {@link #tryReleaseShared} returns true.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryReleaseShared} but is otherwise uninterpreted
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
整體來講,AQS最重要的函式以及流程就是上面這幾個,其他的比如tryLock或者是帶時間的tryLock都是類似的。這裡就不展開贅述了。
參考資料:
http://ifeve.com/understand-condition/
https://blog.csdn.net/pb_yan/article/details/80572194
http://ifeve.com/jdk1-8-abstractqueuedsynchronizer
相關文章
- AQS的原理及原始碼分析AQS原始碼
- 原始碼分析:同步基礎框架——AbstractQueuedSynchronizer(AQS)原始碼框架AQS
- Java併發之AQS原始碼分析(二)JavaAQS原始碼
- java 併發程式設計-AQS原始碼分析Java程式設計AQS原始碼
- AbstractQueuedSynchronizer(AQS)原始碼解析AQS原始碼
- AQS原始碼閱讀AQS原始碼
- 逐行分析AQS原始碼(2)——獨佔鎖的釋放AQS原始碼
- 深入淺出AQS原始碼解析AQS原始碼
- 逐行分析AQS原始碼(3)——共享鎖的獲取與釋放AQS原始碼
- AQS原始碼深入分析之獨佔模式-ReentrantLock鎖特性詳解AQS原始碼模式ReentrantLock
- Java併發:深入淺出AQS之獨佔鎖模式原始碼分析JavaAQS模式原始碼
- 多執行緒高併發程式設計(3) -- ReentrantLock原始碼分析AQS執行緒程式設計ReentrantLock原始碼AQS
- 全網最詳細的AbstractQueuedSynchronizer(AQS)原始碼剖析(一)AQS基礎AQS原始碼
- 從ReentrantLock詳解AQS原理原始碼解析ReentrantLockAQS原始碼
- AQS原始碼探究之競爭鎖資源AQS原始碼
- 併發程式設計之:AQS原始碼解析程式設計AQS原始碼
- 原始碼級深挖AQS佇列同步器原始碼AQS佇列
- Retrofit原始碼分析三 原始碼分析原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- k8s client-go原始碼分析 informer原始碼分析(6)-Indexer原始碼分析K8SclientGo原始碼ORMIndex
- k8s client-go原始碼分析 informer原始碼分析(4)-DeltaFIFO原始碼分析K8SclientGo原始碼ORM
- 5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析Spring原始碼
- Spring原始碼分析——搭建spring原始碼Spring原始碼
- 以太坊原始碼分析(35)eth-fetcher原始碼分析原始碼
- 以太坊原始碼分析(20)core-bloombits原始碼分析原始碼OOM
- 以太坊原始碼分析(24)core-state原始碼分析原始碼