?? AbstractQueuedSynchronizer(AQS),是 Java 并發(fā)包中,實現(xiàn)各種同步結(jié)構(gòu)和部分其他組成單元(如線程池中的 Worker)的基礎(chǔ)。AQS 內(nèi)部數(shù)據(jù)和方法,可以簡單拆分為:
- 一個 volatile 的int成員表征狀態(tài)(state屬性),同時提供了 setState 和 getState 方法。這里之所以用int不用boolean是因為有場景并不是只能有一個線程獲取鎖, 比如CountDownLatch就允許多個線程獲取鎖。
1: ReentrantLock: 用state變量記錄某個線程獲取獨(dú)占鎖的次數(shù),獲取鎖時+1,釋放鎖時-1,在獲取時會校驗線程是否可以獲取鎖。
2:Semaphore: 用state變量作為計數(shù)器,只有在大于0時允許線程進(jìn)入。獲取鎖時-1,釋放鎖時+1。
3: CountDownLatch: 用state變量作為計數(shù)器,在初始化時指定。只要state還大于0,獲取共享鎖會因為失敗而阻塞,直到計數(shù)器的值為0時,共享鎖才允許獲取,所有等待線程會被逐一喚醒。 - 一個先入先出(FIFO)的等待線程隊列,以實現(xiàn)多線程間競爭和等待,這是 AQS 機(jī)制的核心之一。
-
各種基于 CAS 的基礎(chǔ)操作方法,以及各種期望具體同步結(jié)構(gòu)去實現(xiàn)的 tryAcquire/tryRelease 方法。
AbstractQueuedSynchronizer關(guān)鍵屬性
下面來看看acquire獲取鎖的部分源碼:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//定義為final說明子類不能覆蓋這個實現(xiàn)過程, 之類只需要實現(xiàn)tryAcquire方法
public final void acquire(int arg) {
//這里分別會調(diào)用tryAcquire方法用于獲取鎖,如果失敗,會調(diào)用addWaiter方法,將當(dāng)前線的Node加入到FIFO隊列里,通過acquireQueued判斷當(dāng)前節(jié)點(diǎn)是否為頭節(jié)點(diǎn),如果是則試圖獲取鎖, 獲取不成功會加入等待隊列, 并把線程掛起, 不是頭結(jié)點(diǎn)的話直接加入等待隊列并掛起線程。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//這個方法在AbstractQueuedSynchronizer里留給子類來實現(xiàn),
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
}
這里分別會調(diào)用tryAcquire方法用于獲取鎖,如果失敗,會調(diào)用addWaiter方法,將當(dāng)前線的Node加入到FIFO隊列里,通過acquireQueued判斷當(dāng)前節(jié)點(diǎn)是否為頭節(jié)點(diǎn),如果是則試圖獲取鎖, 獲取不成功會加入等待隊列, 并把線程掛起, 不是頭結(jié)點(diǎn)的話直接加入等待隊列并掛起線程。在AbstractQueuedSynchronizer里tryAcquire方法是留給子類來實現(xiàn)的,下面來看看ReentrantLock里的非公平鎖是如何實現(xiàn)這里的邏輯的。源碼如下:
abstract static class Sync extends AbstractQueuedSynchronizer {
//非公平鎖最終最調(diào)用到當(dāng)前這個方法,傳入的acquires為1
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();// 獲取當(dāng)前 AQS 內(nèi)部狀態(tài)量
if (c == 0) { // 0 表示無人占有,則直接用 CAS 修改狀態(tài)位,
if (compareAndSetState(0, acquires)) {// 不檢查排隊情況,直接爭搶
setExclusiveOwnerThread(current); // 并設(shè)置當(dāng)前線程獨(dú)占鎖
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 即使?fàn)顟B(tài)不是 0,也可能當(dāng)前線程是鎖持有者,因為這是重入鎖
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
ReentrantLock的公平鎖的實現(xiàn)邏輯如下:
static final class FairSync extends Sync {
//公平鎖的實現(xiàn)邏輯
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();// 獲取當(dāng)前 AQS 內(nèi)部狀態(tài)量
if (c == 0) {// 0 表示無人占有,
if (!hasQueuedPredecessors() && //這里需要判斷隊列頭節(jié)點(diǎn)的的下一個節(jié)點(diǎn)為空。也就是說不允許線程進(jìn)行插隊。
compareAndSetState(0, acquires)) { //為空才會嘗試獲取鎖
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 即使?fàn)顟B(tài)不是 0,也可能當(dāng)前線程是鎖持有者,因為這是重入鎖
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
接下來再分析 acquireQueued,如果前面的 tryAcquire 失敗,代表著鎖爭搶失敗,進(jìn)入排隊競爭階段。這里就是利用 FIFO 隊列,實現(xiàn)線程間對鎖的競爭的部分,算是AQS 的核心邏輯。源碼如下:
//這個方法是不允許子類重寫的
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //獲取前一個節(jié)點(diǎn)
if (p == head && tryAcquire(arg)) { / 如果前一個節(jié)點(diǎn)是頭結(jié)點(diǎn),則當(dāng)前節(jié)點(diǎn)是FIFO隊列里的第一個節(jié)點(diǎn),則會再次調(diào)用tryAcquire方法嘗試獲取鎖。
setHead(node); //成功,則設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn)
p.next = null; // help GC 清空前面節(jié)點(diǎn)對當(dāng)前節(jié)點(diǎn)的引用
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && //獲取鎖失敗后, 需要判斷當(dāng)前線程是否不要掛起, 正常情況下是都會需要掛起的
parkAndCheckInterrupt())
interrupted = true; //這里需要設(shè)置一個標(biāo)識位, 是因為park方法無法感知到其他線程調(diào)用了當(dāng)前線程的interrupt方法改變了現(xiàn)場的interrupt狀態(tài)
}
} finally {
if (failed) // 獲取鎖失敗,調(diào)用cancelAcquire方法
cancelAcquire(node);
}
}
acquireQueued 的邏輯,簡要來說,就是如果當(dāng)前節(jié)點(diǎn)的前面是頭節(jié)點(diǎn)(AQS里的頭結(jié)點(diǎn)是哨兵結(jié)點(diǎn)),則試圖獲取鎖,一切順利則成為新的頭節(jié)點(diǎn);否則,會掛起線程。上面的部分分析了獲取鎖的邏輯。下面再來看看釋放鎖的邏輯AbstractQueuedSynchronizer的relase源碼如下:
//釋放鎖需要調(diào)用release方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
//釋放成功后,調(diào)用unparkSuccessor方法,讓下個節(jié)點(diǎn)得到鎖
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
//留給具體的子類來實現(xiàn)
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
//當(dāng)前節(jié)點(diǎn)釋放鎖,下個節(jié)點(diǎn)得到鎖狀態(tài)
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//設(shè)置當(dāng)前節(jié)點(diǎn)的waitStatus
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)
Node s = node.next;
//下一個節(jié)點(diǎn)為空情況的處理
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//下一個節(jié)點(diǎn)不為空,則調(diào)用LockSupport的unpark方法喚醒線程
if (s != null)
LockSupport.unpark(s.thread);
}
ReentrantLock里tryRelease的實現(xiàn)如下所示:
protected final boolean tryRelease(int releases) {
int c = getState() - releases; //用state減去release, 這個狀態(tài)是新的狀態(tài)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false; //釋放的結(jié)果用free來表示
if (c == 0) { //為零,表求當(dāng)前沒有錢程點(diǎn)用了,
free = true;
setExclusiveOwnerThread(null);
}
//設(shè)置狀態(tài)
setState(c);
return free;
}
Node的狀態(tài)
java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
/** 表示線程已取消 */
static final int CANCELLED = 1;
/** 表示線程等待喚醒 */
static final int SIGNAL = -1;
/** 表示線程等待獲取同步鎖 */
static final int CONDITION = -2;
/** 表示共享模式下無條件傳播 */
static final int PROPAGATE = -3;
CANCELLED(1):表示當(dāng)前結(jié)點(diǎn)已取消調(diào)度。當(dāng)timeout或被中斷(響應(yīng)中斷的情況下),會觸發(fā)變更為此狀態(tài),進(jìn)入該狀態(tài)后的結(jié)點(diǎn)將不會再變化。
SIGNAL(-1):表示后繼結(jié)點(diǎn)在等待當(dāng)前結(jié)點(diǎn)喚醒。后繼結(jié)點(diǎn)入隊時,會將前繼結(jié)點(diǎn)的狀態(tài)更新為SIGNAL。
CONDITION(-2):表示結(jié)點(diǎn)等待在Condition上,當(dāng)其他線程調(diào)用了Condition的signal()方法后,CONDITION狀態(tài)的結(jié)點(diǎn)將從等待隊列轉(zhuǎn)移到同步隊列中,等待獲取同步鎖。
PROPAGATE(-3):共享模式下,前繼結(jié)點(diǎn)不僅會喚醒其后繼結(jié)點(diǎn),同時也可能會喚醒后繼的后繼結(jié)點(diǎn)。
0:新結(jié)點(diǎn)入隊時的默認(rèn)狀態(tài)。
注意,負(fù)值表示結(jié)點(diǎn)處于有效等待狀態(tài),而正值表示結(jié)點(diǎn)已被取消。所以源碼中很多地方用>0、<0來判斷結(jié)點(diǎn)的狀態(tài)是否正常。
