標籤:upd rup temp clu 檔案中 init ica sub ons
JAVA的眾多鎖的機制,包括Semaphore/ReentrantLock/ReentrantReadWriteLock等都是通過 AQS實現的,因為寫了上述幾個鎖實現的源碼分析,經常使用到AQS的原理和代碼,因此這裡做下AQS的源碼分析。這樣之後再翻看以AQS為基礎的各種各樣的鎖實現就會好理解的多了。
我們結合著源碼檔案中的注釋來看下。
原始碼英文注釋:
/** * Provides a framework for implementing blocking locks and related * synchronizers (semaphores, events, etc) that rely on * first-in-first-out (FIFO) wait queues. This class is designed to * be a useful basis for most kinds of synchronizers that rely on a * single atomic {@code int} value to represent state. Subclasses * must define the protected methods that change this state, and which * define what that state means in terms of this object being acquired * or released. Given these, the other methods in this class carry * out all queuing and blocking mechanics. Subclasses can maintain * other state fields, but only the atomically updated {@code int} * value manipulated using methods {@link #getState}, {@link * #setState} and {@link #compareAndSetState} is tracked with respect * to synchronization. */
提供一個依賴FIFO等待隊列來實現阻塞鎖和關聯同步機制(訊號量、事件等)的架構。該類被作為多數類型的同步機制的一個實用的基礎,設計依賴一個atomic類型的int資料來代表一個狀態。子類必須實現其protected類型的方法來改變這個狀態,這個狀態意味著對象被請求或者釋放的一個說明。提供了這些,本類的其他方法都用來執行所有的隊列和阻塞結構。子類可以維護其他的欄位,但只有state狀態的原子更新操作通過#getState, #setState, #compareAndSetState 被追蹤來實現同步。
AQS的這段類檔案中最開始的注釋,解釋了AQS類的意義和實現手段。
通過一個先進先出的隊列和一個記憶體可見的int類型狀態,來提供一個隊列的阻塞結構,作為眾多同步機制(Semaphore/ReentrantLock等)的基礎演算法結構。
原始碼英文注釋:
<p>This class supports either or both a default <em>exclusive</em> * mode and a <em>shared</em> mode. When acquired in exclusive mode, * attempted acquires by other threads cannot succeed. Shared mode * acquires by multiple threads may (but need not) succeed. This class * does not "understand" these differences except in the * mechanical sense that when a shared mode acquire succeeds, the next * waiting thread (if one exists) must also determine whether it can * acquire as well. Threads waiting in the different modes share the * same FIFO queue. Usually, implementation subclasses support only * one of these modes, but both can come into play for example in a * {@link ReadWriteLock}. Subclasses that support only exclusive or * only shared modes need not define the methods supporting the unused mode.
AQS類支援兩種模式,獨享模式和分享模式,預設是獨享即排它模式。排它模式時,除了當前佔用線程外,其他的線程的嘗試請求將失敗。共用模式下,多個線程的請求將會成功。這個類不能“理解”這種不同,除了這樣一種機械的功能,那就是當一個共用模式的請求成功時,另一個等待線程(如果存在的話)必須決定它是否能夠請求。不同類型的線程等待使用的是同樣的FIFO隊列。一般來說,子類只需要實現其中的一種模式,共用or排它。但也可以一起生效比如ReadWriteLock。只支援一種模式的子類不需要定義另一種不支援的模式的方法。
瞭解了基礎的概括,我們來深入代碼查看具體實現。
static final class Node { /** Marker to indicate a node is waiting in shared mode 標記用來標明一個分享模式的節點 */ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode 標記用來標明一個排他模式的節點 */ static final Node EXCLUSIVE = null; /** waitStatus value to indicate thread has cancelled */ static final int CANCELLED = 1; // 表明線程被關閉的狀態 /** waitStatus value to indicate successor‘s thread needs unparking */ static final int SIGNAL = -1; // 表明線程等待被喚醒的狀態 /** waitStatus value to indicate thread is waiting on condition */ static final int CONDITION = -2; // 表明線程再等待condition條件的狀態 /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ static final int PROPAGATE = -3; // 表明下一個acquireShared會無條件的傳遞 /** * Status field, taking on only the values: * SIGNAL: The successor of this node is (or will soon be) * blocked (via park), so the current node must * unpark its successor when it releases or * cancels. To avoid races, acquire methods must * first indicate they need a signal, * then retry the atomic acquire, and then, * on failure, block. * CANCELLED: This node is cancelled due to timeout or interrupt. * Nodes never leave this state. In particular, * a thread with cancelled node never again blocks. * CONDITION: This node is currently on a condition queue. * It will not be used as a sync queue node * until transferred, at which time the status * will be set to 0. (Use of this value here has * nothing to do with the other uses of the * field, but simplifies mechanics.) * PROPAGATE: A releaseShared should be propagated to other * nodes. This is set (for head node only) in * doReleaseShared to ensure propagation * continues, even if other operations have * since intervened. * 0: None of the above * * The values are arranged numerically to simplify use. * Non-negative values mean that a node doesn‘t need to * signal. So, most code doesn‘t need to check for particular * values, just for sign. * * The field is initialized to 0 for normal sync nodes, and * CONDITION for condition nodes. It is modified using CAS * (or when possible, unconditional volatile writes). */ volatile int waitStatus; /** * Link to predecessor node that current node/thread relies on * for checking waitStatus. Assigned during enqueuing, and nulled * out (for sake of GC) only upon dequeuing. Also, upon * cancellation of a predecessor, we short-circuit while * finding a non-cancelled one, which will always exist * because the head node is never cancelled: A node becomes * head only as a result of successful acquire. A * cancelled thread never succeeds in acquiring, and a thread only * cancels itself, not any other node. */ volatile Node prev; /** * Link to the successor node that the current node/thread * unparks upon release. Assigned during enqueuing, adjusted * when bypassing cancelled predecessors, and nulled out (for * sake of GC) when dequeued. The enq operation does not * assign next field of a predecessor until after attachment, * so seeing a null next field does not necessarily mean that * node is at end of queue. However, if a next field appears * to be null, we can scan prev‘s from the tail to * double-check. The next field of cancelled nodes is set to * point to the node itself instead of null, to make life * easier for isOnSyncQueue. */ volatile Node next; /** * The thread that enqueued this node. Initialized on * construction and nulled out after use. */ volatile Thread thread; /** * Link to next node waiting on condition, or the special * value SHARED. Because condition queues are accessed only * when holding in exclusive mode, we just need a simple * linked queue to hold nodes while they are waiting on * conditions. They are then transferred to the queue to * re-acquire. And because conditions can only be exclusive, * we save a field by using special value to indicate shared * mode. */ Node nextWaiter; /** * Returns true if node is waiting in shared mode. */ final boolean isShared() { return nextWaiter == SHARED; } /** * Returns previous node, or throws NullPointerException if null. * Use when predecessor cannot be null. The null check could * be elided, but is present to help the VM. * * @return the predecessor of this node */ final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
AQS源碼解析