AQS(同步器)是用來(lái)構(gòu)建鎖和其他同步組件的基礎(chǔ)框架锋喜。它的實(shí)現(xiàn)主要是依賴一個(gè)int成員變量來(lái)標(biāo)識(shí)同步狀態(tài)和一個(gè)同步隊(duì)列。同步器本身沒有實(shí)現(xiàn)任何同步接口盼樟,僅僅是定義了幾個(gè)protected修飾同步狀態(tài)的獲取和釋放的方法來(lái)供同步組件使用狸捕。(狀態(tài)的更新使用getState,setState以及compareAndSetState這三個(gè)方法秉犹。)
比如說(shuō)鎖:在鎖的實(shí)現(xiàn)中聚合同步器殖氏,利用同步器實(shí)現(xiàn)鎖的語(yǔ)義晚树。鎖是面向使用者的,它定義了使用者和鎖的接口雅采,但是隱藏了具體的實(shí)現(xiàn)細(xì)節(jié)爵憎。而同步器是面向鎖的實(shí)現(xiàn)著,它簡(jiǎn)化了鎖的實(shí)現(xiàn)方式婚瓜,屏蔽了同步狀態(tài)的管理宝鼓,線程的排隊(duì)、等待和喚醒等操作巴刻。
AQS使用模板方法設(shè)計(jì)模式愚铡,它將一些方法開放給子類去進(jìn)行重寫,而同步器給同步組件提供的模板方法又會(huì)重新調(diào)用子類重寫的方法胡陪。例如:tryAcquire()
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
ReentrantLock中NonfairSync(繼承AQS)會(huì)重寫該方法為:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
而AQS中的模板方法acquire():
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AQS提供的模板方法分為三類:
- 獨(dú)占式獲取與釋放同步狀態(tài)茂附;
- 共享式獲取與釋放同步狀態(tài);
- 查詢同步隊(duì)列中等待線程情況督弓;
AQS本身內(nèi)部定義一個(gè)靜態(tài)類Node,通過(guò)雙向鏈?zhǔn)浇Y(jié)構(gòu)構(gòu)成了同步隊(duì)列。Node維護(hù)了幾個(gè)屬性:
volatile int waitStatus //節(jié)點(diǎn)狀態(tài)
volatile Node prev //當(dāng)前節(jié)點(diǎn)/線程的前驅(qū)節(jié)點(diǎn)
volatile Node next; //當(dāng)前節(jié)點(diǎn)/線程的后繼節(jié)點(diǎn)
volatile Thread thread;//加入同步隊(duì)列的線程引用
Node nextWaiter;//等待隊(duì)列中的下一個(gè)節(jié)點(diǎn)
節(jié)點(diǎn)的狀態(tài):
int CANCELLED = 1//節(jié)點(diǎn)從同步隊(duì)列中取消
int SIGNAL = -1//后繼節(jié)點(diǎn)的線程處于等待狀態(tài)乒验,
//如果當(dāng)前節(jié)點(diǎn)釋放同步狀態(tài)會(huì)通知后繼節(jié)點(diǎn)愚隧,使得后繼節(jié)點(diǎn)的線程能夠運(yùn)行;
int CONDITION = -2//當(dāng)前節(jié)點(diǎn)進(jìn)入等待隊(duì)列中
int PROPAGATE = -3//表示下一次共享式同步狀態(tài)獲取將會(huì)無(wú)條件傳播下去
int INITIAL = 0;//初始狀態(tài)
AQS重要成員變量:
private transient volatile Node head;
private transient volatile Node tail;
AQS實(shí)際上是通過(guò)頭尾指針來(lái)管理控制同步隊(duì)列,同時(shí)實(shí)現(xiàn)對(duì)獲取鎖失敗的線程進(jìn)行入隊(duì)操作狂塘,釋放鎖是完成對(duì)同步隊(duì)列中等待的線程進(jìn)行通知等核心操作录煤。
獨(dú)占鎖獲取鎖的分析
先調(diào)用acquire()方法,看是否獲取同步狀態(tài)(也就是是否加鎖成功)荞胡,如果成功直接返回妈踊,如果失敗則在調(diào)用addWatier(),然后在調(diào)用acquireQueued()方法。
public final void acquire(int arg) {
//先看同步狀態(tài)是否獲取成功泪漂,如果成功則方法結(jié)束返回
//若失敗則先調(diào)用addWaiter()方法再調(diào)用acquireQueued()方法
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter()源碼如下:
private Node addWaiter(Node mode) {
// 1. 將當(dāng)前線程構(gòu)建成Node類型
Node node = new Node(Thread.currentThread(), mode);
// 2. 當(dāng)前尾節(jié)點(diǎn)是否為null廊营?
Node pred = tail;
if (pred != null) {
// 2.2 將當(dāng)前節(jié)點(diǎn)尾插入的方式插入同步隊(duì)列中
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 2.1. 當(dāng)前同步隊(duì)列尾節(jié)點(diǎn)為null,
//說(shuō)明當(dāng)前線程是第一個(gè)加入同步隊(duì)列進(jìn)行等待的線程
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//1. 構(gòu)造頭結(jié)點(diǎn)
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 2. 尾插入萝勤,CAS操作失敗自旋嘗試
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
addWaiter()露筒、enq()分析如下:
- 當(dāng)前同步對(duì)列的尾結(jié)點(diǎn)為null,調(diào)用enq()方法插入當(dāng)前節(jié)點(diǎn)
- 當(dāng)前對(duì)列的尾結(jié)點(diǎn)不為null敌卓,則采用compareAndSetTail()把當(dāng)前節(jié)點(diǎn)插入同步隊(duì)列的尾部慎式,如果則采用compareAndSetTail失敗則繼續(xù)執(zhí)行enq()方法,里面會(huì)繼續(xù)采用自旋模式繼續(xù)插入趟径,直至成功為止瘪吏。
當(dāng)前節(jié)點(diǎn)(線程)已經(jīng)插入同步隊(duì)列,acquireQueued()方法保證同步隊(duì)列的節(jié)點(diǎn)獲取獨(dú)占鎖(排隊(duì)獲取鎖的過(guò)程)蜗巧。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 1. 獲得當(dāng)前節(jié)點(diǎn)的先驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
// 2. 當(dāng)前節(jié)點(diǎn)能否獲取獨(dú)占式鎖
// 2.1 如果當(dāng)前節(jié)點(diǎn)的先驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn)
//并且成功獲取同步狀態(tài)掌眠,即可以獲得獨(dú)占式鎖
if (p == head && tryAcquire(arg)) {
//隊(duì)列頭指針用指向當(dāng)前節(jié)點(diǎn)
setHead(node);
//釋放前驅(qū)節(jié)點(diǎn)
p.next = null; // help GC
failed = false;
return interrupted;
}
// 2.2 獲取鎖失敗,線程進(jìn)入等待狀態(tài)等待獲取獨(dú)占式鎖
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
分析入下:
- 首先獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)惧蛹,如果先去節(jié)點(diǎn)是頭結(jié)點(diǎn)并且已經(jīng)成功獲取同步狀態(tài)(成功獲取鎖)扇救,當(dāng)前節(jié)點(diǎn)也能獲取鎖,反之進(jìn)入等待狀態(tài)香嗓。
- 如果當(dāng)前節(jié)點(diǎn)前驅(qū)幾點(diǎn)已經(jīng)獲取鎖迅腔,然后通過(guò)setHead()將當(dāng)前節(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn),前驅(qū)節(jié)點(diǎn)出對(duì)靠娱,與同步隊(duì)列斷開沧烈,方便回收。
- 如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)沒有獲取同步狀態(tài),shouldParkAfterFailedAcquire()方法像云,通過(guò)compareAndSetWaitStatus設(shè)置當(dāng)前節(jié)點(diǎn)為signall也就是等待狀態(tài)锌雀。
- parkAndCheckInterrupt()調(diào)用此方法通過(guò)LockSupport.part()將當(dāng)前線程阻塞。
獨(dú)占鎖釋放分析
release方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
unparkSuccessor(h)
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)
Node s = node.next;
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;
}
if (s != null)
//后繼節(jié)點(diǎn)不為null時(shí)喚醒該線程
LockSupport.unpark(s.thread);
}
分析如下:
- compareAndSetWaitStatus()通過(guò)cas操作更改同步對(duì)列的同步狀態(tài)迅诬。
- 獲取當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)腋逆,如果后繼節(jié)點(diǎn)不為空,則調(diào)用LockSupport.unpark()喚醒后繼節(jié)點(diǎn)包裝的線程侈贷。
總結(jié):
- 線程獲取鎖失敗惩歉,線程被封裝成Node進(jìn)行入隊(duì)操作,核心方法在于addWaiter()和enq(),同時(shí)enq()完成對(duì)同步隊(duì)列的頭結(jié)點(diǎn)初始化工作以及CAS操作失敗的重試;
- 線程獲取鎖是一個(gè)自旋的過(guò)程撑蚌,當(dāng)且僅當(dāng) 當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn)并且成功獲得同步狀態(tài)時(shí)上遥,節(jié)點(diǎn)出隊(duì)即該節(jié)點(diǎn)引用的線程獲得鎖,否則争涌,當(dāng)不滿足條件時(shí)就會(huì)調(diào)用LookSupport.park()方法使得線程阻塞粉楚;
- 釋放鎖的時(shí)候會(huì)喚醒后繼節(jié)點(diǎn);
總體來(lái)講:
在獲取同步狀態(tài)時(shí)亮垫,AQS維護(hù)一個(gè)同步隊(duì)列模软,獲取同步狀態(tài)失敗的線程會(huì)加入到隊(duì)列中進(jìn)行自旋;移除隊(duì)列(或停止自旋)的條件是前驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn)并且成功獲得了同步狀態(tài)包警。在釋放同步狀態(tài)時(shí)撵摆,同步器會(huì)調(diào)用unparkSuccessor()方法喚醒后繼節(jié)點(diǎn)。