上面幾章節介紹了讀寫鎖和Sync抽象類,這節介紹AQS如何控制同步的
1.首先看一下類的定義
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable
官方對類的描述爲:提供了一個框架實現阻塞鎖,依賴於同步器(信號量、事件等)以及FIFO等待隊列。該類爲大多數同步器提供了一個有用的基礎。
2.變量
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;//等待隊列的頭
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;//等待隊列的尾
/**
* The synchronization state.
*/
private volatile int state;//同步狀態,鎖個數
3.看一下Node節點的內容
static final class 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;
/**
* 等待狀態的枚舉值,表示下一個acquireShared應該無條件的傳播,用於將喚醒後繼線程傳遞下去,這個狀態的引入是爲了完善和增強共享鎖的喚醒機制。在一個節點成爲頭節點之前,是不會躍遷爲此狀態的
*/
static final int PROPAGATE = -3;
/**
* 等待狀態
*/
volatile int waitStatus;
/**
* 前一個節點
*/
volatile Node prev;
/**
* 下一個節點
*/
volatile Node next;
/**
* 線程
*/
volatile Thread thread;
/**
* 下一個等待者
*/
Node nextWaiter;
/**
* 是否處於共享模式
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 返回前一個節點,如果爲空,則拋空指針異常
*
* @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;
}
}
4.acquireShared,獲取讀鎖時用到
public final void acquireShared(int arg) {//在共享模式中獲取鎖
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared方法見Sync的實現,如果返回<0,說明獲取鎖失敗,會調用doAcquireShared方法
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);//等待隊列中增加一個waiter
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {//獲取鎖成功
setHeadAndPropagate(node, r);//設置head節點且喚醒下一個線程或者傳播狀態
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//獲取失敗,則檢查並更新狀態
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);//取消獲取鎖
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//設置head
/*
* 喚醒隊列中的下一個節點
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
/*
*
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {//存在後續線程
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);//喚醒
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
5.tryReadLock
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;//如果其他線程擁有寫鎖,則返回false
int r = sharedCount(c);
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");//如果讀鎖的個數超過最大,則報異常
if (compareAndSetState(c, c + SHARED_UNIT)) {//如果設置狀態成功
if (r == 0) {//如果讀鎖爲0,設置第一個獲取到讀鎖的線程
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//同一個線程,則讀鎖的個數加1
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();//從threadLocal中獲取鎖個數
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}
總結一下AQS實現的大致思路:
AQS內部維護了一個隊列管理鎖。
線程會首先嚐試獲取鎖,如果獲取失敗,則將當前線程以及等待狀態包裝成Node節點增加到等待隊列中。
接着不斷嘗試獲取鎖,如果失敗,則阻塞自己,直到被喚醒。
當持有鎖的線程釋放鎖時,會喚醒隊列中的後續線程。